본문 바로가기
Python/Flask

[Day 9] WMS(Warehouse Management System) 구성 및 요구 사항 정의 4

by 깨준 2024. 7. 25.

정보관리(Dashboard)

  • 보고서 생성: 재고 현황, 입출고 현황등을 실시간으로 보고서로 생성합니다.
  • 통계 및 분석: 데이터를 분석하여 효율적인 창고 운영 전략을 수립합니다.

 

연습문제 1~2

 

문제 1 : 다음은 Matplotlib를 이용하여 시각화하는 내용이다 다음의 요구사항에 맞춰 개발을 완료하세요. 또한 입고, 재고, 출고 현황은 하나의 화면에 배치해서 만들고 웹에서 출력할 수 있도록 한다.

(app.py와 dashboard.html로 구현)

  • 입고 테이블을 읽어서 입고현황 Chart로 만드세요.
  • 재고 테이블을 읽어서 재고현황 Chart를 만드세요
  • 출고 테이블을 읽어서 출고현황 Chart를 만드세요.

app.py

from flask import Flask, render_template, url_for, send_from_directory
import mysql.connector
from mysql.connector import Error
import os
import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'static/images'

def create_connection():
    connection = None
    try:
        connection = mysql.connector.connect(
            host='localhost',
            user='joon',
            password='1234',
            database='backend'
        )
    except Error as e:
        print(f"Error: '{e}'")
    return connection

def fetch_data(query):
    connection = create_connection()
    if connection is None:
        return []

    try:
        cursor = connection.cursor(dictionary=True)
        cursor.execute(query)
        records = cursor.fetchall()
        cursor.close()
    except Error as e:
        print(f"Error: '{e}'")
        records = []
    finally:
        connection.close()
    
    return records

def save_chart(df, filename, title, xlabel, ylabel, id_col, value_col, color='blue'):
    if not os.path.exists(app.config['UPLOAD_FOLDER']):
        os.makedirs(app.config['UPLOAD_FOLDER'])

    fig, ax = plt.subplots(figsize=(4, 3))  # 이미지 크기 조정
    ax.bar(df[id_col].astype(str), df[value_col], color=color)
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_xticklabels(df[id_col].astype(str), rotation=0)
    fig.tight_layout()

    canvas = FigureCanvas(fig)
    img_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    canvas.print_figure(img_path)

    return img_path

@app.route('/incoming_chart')
def incoming_chart():
    query = "SELECT id, product_quantity FROM incoming_ex"
    records = fetch_data(query)
    df = pd.DataFrame(records)
    
    if not df.empty:
        img_path = save_chart(df, 'incoming_chart.png', 'Incoming Records', 'ID', 'Quantity', 'id', 'product_quantity', color='blue')
        return send_from_directory(app.config['UPLOAD_FOLDER'], 'incoming_chart.png')
    else:
        return "No data available"

@app.route('/stock_chart')
def stock_chart():
    query = "SELECT id, stock_quantity FROM stock_ex"
    records = fetch_data(query)
    df = pd.DataFrame(records)
    
    if not df.empty:
        img_path = save_chart(df, 'stock_chart.png', 'Stock Records', 'ID', 'Quantity', 'id', 'stock_quantity', color='green')
        return send_from_directory(app.config['UPLOAD_FOLDER'], 'stock_chart.png')
    else:
        return "No data available"

@app.route('/outbound_chart')
def outbound_chart():
    query = "SELECT id, actual_quantity FROM outbound_registration_ex"
    records = fetch_data(query)
    df = pd.DataFrame(records)
    
    if not df.empty:
        img_path = save_chart(df, 'outbound_chart.png', 'Outbound Records', 'ID', 'Quantity', 'id', 'actual_quantity', color='red')
        return send_from_directory(app.config['UPLOAD_FOLDER'], 'outbound_chart.png')
    else:
        return "No data available"

@app.route('/')
def index():
    incoming_img = url_for('incoming_chart')
    stock_img = url_for('stock_chart')
    outbound_img = url_for('outbound_chart')
    return render_template('dashboard.html', incoming_img=incoming_img, stock_img=stock_img, outbound_img=outbound_img)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5001, debug=True)

 

dashboard.html

<!DOCTYPE html>
<html>
<head>
    <title>Warehouse Dashboard</title>
</head>
<body>
    <h1>Warehouse Dashboard</h1>
    <div>
        <h2>Incoming Chart</h2>
        <img src="{{ incoming_img }}" alt="Incoming Chart">
    </div>
    <div>
        <h2>Stock Chart</h2>
        <img src="{{ stock_img }}" alt="Stock Chart">
    </div>
    <div>
        <h2>Outbound Chart</h2>
        <img src="{{ outbound_img }}" alt="Outbound Chart">
    </div>
</body>
</html>

입고, 재고, 출고 그래프

 

문제 2 : 같은 내용에 대해 그래프를 3종류(파이, 꺽은선, 막대)로 만들고 버튼으로 선택할 수 있도록 하세요.

  • 입고, 재고, 출고에 대한 현황을 각각 3종류의 그래프로 그린다.
  • 버튼을 선택하면 선택된 종류의 그래프만 출력한다.

 

app.py (추가)

# 차트 생성 함수
def save_chart(df, filename, title, xlabel, ylabel, id_col, value_col, chart_type='bar', color='blue'):
    if not os.path.exists(app.config['UPLOAD_FOLDER']):
        os.makedirs(app.config['UPLOAD_FOLDER'])

    fig, ax = plt.subplots(figsize=(3, 2))  # 이미지 크기 조정
    
    if chart_type == 'bar':
        ax.bar(df[id_col].astype(str), df[value_col], color=color)
    elif chart_type == 'line':
        ax.plot(df[id_col].astype(str), df[value_col], color=color, marker='o')
    elif chart_type == 'pie':
        colors = plt.cm.Paired(range(len(df)))  # Use the 'Paired' colormap to get a range of colors
        ax.pie(df[value_col], labels=df[id_col].astype(str), autopct='%1.1f%%', colors=colors)
        ax.axis('equal')  # Equal aspect ratio ensures that pie is drawn as a circle.
        ax.set_title(title)
        fig.tight_layout()
        img_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        canvas = FigureCanvas(fig)
        canvas.print_figure(img_path)
        return img_path

    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_xticklabels(df[id_col].astype(str), rotation=0)
    fig.tight_layout()

    canvas = FigureCanvas(fig)
    img_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    canvas.print_figure(img_path)

    return img_path


@app.route('/incoming_chart/<chart_type>')
def incoming_chart(chart_type):
    query = "SELECT id, product_quantity FROM incoming_ex"
    records = fetch_data(query)
    df = pd.DataFrame(records)
    
    if not df.empty:
        img_path = save_chart(df, f'incoming_{chart_type}_chart.png', 'Incoming Records', 'ID', 'Quantity', 'id', 'product_quantity', chart_type=chart_type, color='blue')
        return send_from_directory(app.config['UPLOAD_FOLDER'], f'incoming_{chart_type}_chart.png')
    else:
        return "No data available"

@app.route('/stock_chart/<chart_type>')
def stock_chart(chart_type):
    query = "SELECT id, stock_quantity FROM stock_ex"
    records = fetch_data(query)
    df = pd.DataFrame(records)
    
    if not df.empty:
        img_path = save_chart(df, f'stock_{chart_type}_chart.png', 'Stock Records', 'ID', 'Quantity', 'id', 'stock_quantity', chart_type=chart_type, color='green')
        return send_from_directory(app.config['UPLOAD_FOLDER'], f'stock_{chart_type}_chart.png')
    else:
        return "No data available"

@app.route('/outbound_chart/<chart_type>')
def outbound_chart(chart_type):
    query = "SELECT id, actual_quantity FROM outbound_registration_ex"
    records = fetch_data(query)
    df = pd.DataFrame(records)
    
    if not df.empty:
        img_path = save_chart(df, f'outbound_{chart_type}_chart.png', 'Outbound Records', 'ID', 'Quantity', 'id', 'actual_quantity', chart_type=chart_type, color='red')
        return send_from_directory(app.config['UPLOAD_FOLDER'], f'outbound_{chart_type}_chart.png')
    else:
        return "No data available"

@app.route('/')
def dashboard():
    incoming_bar_img = url_for('incoming_chart', chart_type='bar')
    incoming_line_img = url_for('incoming_chart', chart_type='line')
    incoming_pie_img = url_for('incoming_chart', chart_type='pie')
    stock_bar_img = url_for('stock_chart', chart_type='bar')
    stock_line_img = url_for('stock_chart', chart_type='line')
    stock_pie_img = url_for('stock_chart', chart_type='pie')
    outbound_bar_img = url_for('outbound_chart', chart_type='bar')
    outbound_line_img = url_for('outbound_chart', chart_type='line')
    outbound_pie_img = url_for('outbound_chart', chart_type='pie')
    
    return render_template('dashboard.html', 
                           incoming_bar_img=incoming_bar_img, incoming_line_img=incoming_line_img, incoming_pie_img=incoming_pie_img,
                           stock_bar_img=stock_bar_img, stock_line_img=stock_line_img, stock_pie_img=stock_pie_img,
                           outbound_bar_img=outbound_bar_img, outbound_line_img=outbound_line_img, outbound_pie_img=outbound_pie_img)

 

dashboard.html(수정)

<div class="container">
        <nav>
            <div class="nav-left">
                <h2>GJ Lecture</h2>
            </div>
            <ul>
                <li><a href="{{ url_for('enterRegist') }}">입고등록</a></li>
                <li><a href="{{ url_for('enterView') }}">입고조회</a></li>
                <li><a href="{{ url_for('stockView') }}">재고조회</a></li>
                <li><a href="{{ url_for('stockManage') }}">재고관리</a></li>
                <li><a href="{{ url_for('outboundRegist') }}">출고등록</a></li>
                <li><a href="{{ url_for('outboundView') }}">출고조회</a></li>
                <li><a href="{{ url_for('dashboard') }}">통계 및 분석</a></li>
            </ul>
        </nav>
        <main>
            <div class="chart-section">
                <div class="chart-header">
                    <h2>입고 Charts</h2>
                </div>
                <div class="chart-buttons">
                    <button onclick="showChart('incoming', 'bar')">입고 막대 그래프</button>
                    <button onclick="showChart('incoming', 'line')">입고 꺽은선 그래프</button>
                    <button onclick="showChart('incoming', 'pie')">입고 파이 그래프</button>
                </div>
                <div class="chart-container">
                    <div id="incoming-bar" class="chart-container-item" style="display: none;">
                        <h3>입고 Bar Chart</h3>
                        <img src="{{ incoming_bar_img }}" alt="Incoming Bar Chart">
                    </div>
                    <div id="incoming-line" class="chart-container-item" style="display: none;">
                        <h3>입고 Line Chart</h3>
                        <img src="{{ incoming_line_img }}" alt="Incoming Line Chart">
                    </div>
                    <div id="incoming-pie" class="chart-container-item" style="display: block;">
                        <h3>입고 Pie Chart</h3>
                        <img src="{{ incoming_pie_img }}" alt="Incoming Pie Chart">
                    </div>
                </div>
            </div>
        
            <div class="chart-section">
                <div class="chart-header">
                    <h2>재고 Charts</h2>
                </div>
                <div class="chart-buttons">
                    <button onclick="showChart('stock', 'bar')">재고 막대 그래프</button>
                    <button onclick="showChart('stock', 'line')">재고 꺽은선 그래프</button>
                    <button onclick="showChart('stock', 'pie')">재고 파이 그래프</button>
                </div>
                <div class="chart-container">
                    <div id="stock-bar" class="chart-container-item" style="display: none;">
                        <h3>재고 Bar Chart</h3>
                        <img src="{{ stock_bar_img }}" alt="Stock Bar Chart">
                    </div>
                    <div id="stock-line" class="chart-container-item" style="display: none;">
                        <h3>재고 Line Chart</h3>
                        <img src="{{ stock_line_img }}" alt="Stock Line Chart">
                    </div>
                    <div id="stock-pie" class="chart-container-item" style="display: block;">
                        <h3>재고 Pie Chart</h3>
                        <img src="{{ stock_pie_img }}" alt="Stock Pie Chart">
                    </div>
                </div>
            </div>
        
            <div class="chart-section">
                <div class="chart-header">
                    <h2>출고 Charts</h2>
                </div>
                <div class="chart-buttons">
                    <button onclick="showChart('outbound', 'bar')">출고 막대 그래프</button>
                    <button onclick="showChart('outbound', 'line')">출고 꺽은선 그래프</button>
                    <button onclick="showChart('outbound', 'pie')">출고 파이 그래프</button>
                </div>
                <div class="chart-container">
                    <div id="outbound-bar" class="chart-container-item" style="display: none;">
                        <h3>출고 Bar Chart</h3>
                        <img src="{{ outbound_bar_img }}" alt="Outbound Bar Chart">
                    </div>
                    <div id="outbound-line" class="chart-container-item" style="display: none;">
                        <h3>출고 Line Chart</h3>
                        <img src="{{ outbound_line_img }}" alt="Outbound Line Chart">
                    </div>
                    <div id="outbound-pie" class="chart-container-item" style="display: block;">
                        <h3>출고 Pie Chart</h3>
                        <img src="{{ outbound_pie_img }}" alt="Outbound Pie Chart">
                    </div>
                </div>
            </div>
        
            <script>
                function showChart(section, type) {
                    // 모두 지우기
                    var charts = document.querySelectorAll('#' + section + '-bar, #' + section + '-line, #' + section + '-pie');
                    charts.forEach(function(chart) {
                        chart.style.display = 'none';
                    });
                    // 선택된 것만 그리기
                    document.getElementById(section + '-' + type).style.display = 'block';
                }
            </script>
        </main>
    </div>

통계 및 분석 페이지


오늘은 WMS(Warehouse Management System) 수업 마지막으로 통계를 분석하여 시각화하는 공부를 하였다. 예전에 데이터 분석 공부를 잠깐 해서 시각화를 몇 번해봤었는데 오늘처럼 웹으로 시각화한 것을 올린 경험을 처음이다. 시각화한 것으로 웹에 띄우니깐 더 재미있었다. 아직 분석 코드를 이해를 못 했지만 추후 반복연습을 통해 데이터 분석 코드를 공부해야 될 것 같다.