dmswo 2024. 12. 8. 18:01

 

1. 정중앙에 로그인 창이 뜨도록 수정

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            font-family: Arial, sans-serif;
            background-color: #f0f0f0;
        }
        .login-container {
            background-color: white;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            width: 300px;
        }
        h2 {
            text-align: center;
            margin-bottom: 20px;
        }
        label {
            display: block;
            margin-bottom: 8px;
        }
        input {
            width: 100%;
            padding: 10px;
            margin-bottom: 15px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            width: 100%;
            padding: 10px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            font-size: 16px;
        }
        button:hover {
            background-color: #45a049;
        }
    </style>
</head>
<body>
    <div class="login-container">
        <h2>Login</h2>
        <form method="POST" action="/">
            <label for="user_id">ID: </label>
            <input type="text" name="user_id" required><br>
            <label for="password">Password: </label>
            <input type="password" name="password" required><br>
            <button type="submit">Login</button>
        </form>
    </div>
</body>
</html>

 

1

2. 로그인에 실패하면 실패를 알리는 창이 뜨도록 수정 -> 확인을 누르면 다시 로그인을 시도할 수 있다.

2. 잘못된 로그인 정보 입력
2. 로그인 실패 팝업

 

2-1.

잘못된 로그인 정보가 입력되면 flash를 사용하여 로그인 실패 메시지를 표시한다.

로그인 실패 시 flash를 호출하고, 이는 login.html 템플릿에서 get_flashed_messages로 가져와서 JavaScript에서 팝업을 띄운다.

 

login.html에 추가한 코드

    <script>
    window.onload = function() {
        // Flask에서 전달받은 flash 메시지를 JavaScript 변수로 전달
        var messages = {{ get_flashed_messages() | tojson }};
        if (messages.length > 0) {
            console.log("Flash messages: ", messages);
            alert(messages[0]);  // 첫 번째 메시지를 팝업으로 표시
        } else {
            console.log("No flash messages.");
        }
    }
    </script>

 

3. 영화 관리 시스템은 사용자(고객, 스태프, 분석가) 별로 다른 기능을 제공한다.

따라서 고객이 로그인 하면 고객의 기능들을 보여주는 html 파일로 넘어간다.

이렇게 사용자별로 html 페이지를 다르게 만들었다. (전에 사용했던 dashboard.html은 삭제)

여러 기능 중  원하는 기능을 클릭하면 해당 기능을 수행하는 페이지로 이동할 수 있도록 링크를 걸어놨다.

 

처음 만들었던 영화 관리 시스템은 터미널 기반이라 영화 포스터를 볼 수 없었다.

지금은 영화를 조회할 때 포스터까지 볼 수 있게 하겠다.

SQLite 데이터베이스에서 URL을 저장하려면 TEXT 자료형을 사용하면 된다. 

3. 새 컬럼 추가
3. URL 추가
3. 사진 추가 완료

 


이제 고객의 첫 번째 기능인 모든 상영 예정인 영화 조회하기를 구현하는 코드를 적고 이 기능을 구현하면서 생긴 오류와 해결과정을 적겠다. 첫 기능이니 모든 코드를 첨부하고 다음 기능부터는 추가되는 코드 부분만 첨부하겠다.

고객 - 모든 상영 예정인 영화 조회 

app.py

추가된 코드 없다.

  GNU nano 7.2                                             app.py

from flask import Flask
from routes import login_route

app = Flask(__name__)
app.secret_key = 'c7IVzYZekc'


login_route(app)

if __name__ == "__main__":

    app.run(host='0.0.0.0', port=5000, debug=True)

routes.py

view_movies 함수는 고객의 첫번째 기능을 선택하면 실행된다.  

view_movies 함수에서 models.py에 작성한 print_all_movie 함수를 호출해 모든 영화의 정보를 가져온다.

가져온 정보를 movies.html로 전달해서 출력한다.

  GNU nano 7.2                                            routes.py
from flask import render_template, request, redirect, url_for, flash
from models import fetch_members_data, distinguish_user, print_all_movie

def login_route(app):
    @app.route("/", methods=["GET", "POST"])
    def login():
        if request.method == "POST":
            input_id = request.form['user_id']
            input_password = request.form['password']

            members_data = fetch_members_data()

            for member in members_data:
                if input_id == member[0] and input_password == member[1]:
                    user_type = distinguish_user(input_id)
                    if user_type:
                        return redirect(url_for('dashboard', user_type=user_type, name=member[2]))
                    else:
                        flash("User type could not be determined.")
                        return redirect(url_for('login'))

            flash("Login failed. ID and password do not match.")
            return redirect(url_for('login'))

        return render_template("login.html")

    @app.route("/dashboard/<user_type>/<name>")
    def dashboard(user_type, name):
        if user_type == "user":
            return render_template("user_function.html", user_type=user_type, name=name)
        elif user_type == "staff":
            return render_template("staff_function.html", user_type=user_type, name=name)
        elif user_type == "analyst":
            return render_template("analyst_function.html", user_type=user_type, name=name)

    @app.route('/movies')
    def view_movies():
        movies = print_all_movie()
        return render_template('movies.html', movies=movies)

 

models.py

print_all_movie 함수를 추가했고 이 함수는 상영 예정인 모든 영화의 특정 정보를 반환한다.

import sqlite3

def get_db_connection():
    """SQLite 데이터베이스 연결 함수"""
    conn = sqlite3.connect('movies.db') #movies.db는 프로젝트 디렉터리에 저장된 파일
    #conn.row_factory = sqlite3.Row #딕셔너리 형태로 결과를 반환하도록 설정
    return conn

def print_all_movie():
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("""SELECT DISTINCT m.movie_id, m.movie_title, s.theater_id, s.screening_date, m.poster_url
            FROM movie m JOIN screeningschedule s ON m.movie_id = s.movie_id
            WHERE s.screening_date >= CURRENT_DATE""")
    result = cursor.fetchall()
    conn.close()
    return result
def fetch_members_data():
    """회원 데이터 가져오기"""
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT id, password, name FROM Member")
    members_data_from_db = cursor.fetchall()
    return members_data_from_db

def distinguish_user(input_id):
    """사용자 유형 구분하기"""
    if "user" in input_id:
        return "user"
    elif "staff" in input_id:
        return "staff"
    elif "analyst" in input_id:
        return "analyst"

user_function.html

사용자 기능 중 첫 번째인 모든 상영 예정인 영화 조회를 클릭하면 /movies 링크로 넘어간다. 

이 링크는 routes.py에 구현했듯이 view_movies 함수를 살행한다.

  GNU nano 7.2                                       user_function.html                                                 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>User Function</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            background-color: #f4f4f9;
            color: #333;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
        }
        .container {
            background-color: white;
            padding: 20px 30px;
            border-radius: 10px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            text-align: center;
            max-width: 400px;
            width: 100%;
        }
        h2 {
            margin-bottom: 20px;
            color: #4CAF50;
        }
        p {
            margin: 10px 0;
            font-size: 16px;
        }
        .link-list {
            list-style: none;
            padding: 0;
        }
        .link-list li {
            margin: 10px 0;
        }
        .link-list a {
            text-decoration: none;
            color: #4CAF50;
            font-weight: bold;
            transition: color 0.2s ease;
        }
        .link-list a:hover {
            color: #388E3C;
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>환영합니다, {{ name }}님!</h2>
        <p>고객님 원하시는 기능을 선택해주세요.</p>
        <ul class="link-list">
            <li><a href="/movies">모든 상영 예정인 영화 조회</a></li>
            <li><a href="/movies/search">영화 이름으로 상영 예정인 영화 조회</a></li>
            <li><a href="/movies/title-length">영화 제목 글자 수로 상영 예정인 영화 조회</a></li>
            <li><a href="/movies/sort/title">영화 제목 기준으로 상영 예정인 영화를 오름차순 정렬</a></li>
            <li><a href="/movies/sort/date">영화 상영 날짜 기준으로 상영 예정인 영화를 내림차순 정렬</a></li>
            <li><a href="/movies/book">영화 예매</a></li>
            <li><a href="/movies/cancel">영화 취소</a></li>
            <li><a href="/logout">프로그램 종료</a></li>
        </ul>
    </div>
</body>
</html>

movies.html -> print_all_movies.html

영화 정보를 받아서 출력했다. (링크를 통해 포스터 출력도 했다.)

사용자 별로 기능이 많아 사용자별로 폴더를 따로 만들고 html 파일들을 보관했다.

따라서 movies.html 은 /user/print_all_movies.html 으로 위치와 이름을 변경하였다. (다음 블로그 글에 소개)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>All Movies</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f9f9f9;
            margin: 0;
            padding: 20px;
        }
        h1 {
            text-align: center;
            color: #333;
        }
        .movie-list {
            max-width: 800px;
            margin: 0 auto;
        }
        .movie-item {
            display: flex;
            align-items: center;
            background: #fff;
            margin: 15px 0;
            padding: 20px;
            border: 1px solid #ddd;
            border-radius: 6px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }
        .movie-item img {
            width: 100px;
            height: 150px;
            margin-right: 20px;
            object-fit: cover;
            border-radius: 4px;
            border: 1px solid #ccc;
        }
        .movie-details {
            margin: 0;
            padding: 0;
            list-style: none;
        }
        .movie-details li {
            margin: 5px 0;
            color: #666;
        }
        h2 {
            margin: 0 0 10px;
            color: #444;
        }
    </style>
</head>
<body>
    <h1>All Movies</h1>
    <div class="movie-list">
        {% for movie in movies %}
        <div class="movie-item">
            <!-- Movie poster -->
            <img src="{{ movie[4] }}" alt="Poster for {{ movie[1] }}">
            <div>
                <h2>{{ movie[1] }}</h2> <!-- Movie title -->
                <ul class="movie-details">
                    <li><strong>Movie ID:</strong> {{ movie[0] }}</li>
                    <li><strong>Theater ID:</strong> {{ movie[2] }}</li>
                    <li><strong>Screening Date:</strong> {{ movie[3] }}</li>
                </ul>
            </div>
        </div>
        {% endfor %}
    </div>
</body>
</html>

 

3. 여러개 영화가 출력된다.

 

 

3-1 오류 : 고객 기능 중에 1~5번은 상영 예정인 영화를 조회하고 정렬하는 것이다.

상영 예정인 영화가 대상이기 때문에 screening_date >= current_date 조건을 만족해야한다.

그런데 SQL문을 2023년도에 만들어 놓아서 screening_date 는 2023년도였고 current_date는 2024년도 였다.

따라서 조건을 만족하지 못해 결과가 안나왔다. 

 

해결방법 : SQL문에서 screeing_date를 2025년으로 바꾸고 다시 업로드했다.

3-1. 아무것도 나오지 않아서 date에 문제가 있음을 알았다.
3-1 오류 수정

 

3-2. 오류 : 모든 상영 예정인 영화가 출력 되어야 하는데 sqlite3.Row 객체가 출력됐다. 

 

해결방법 : conn.row_factory를 설정하지 않으면 fetchall()은 튜플로 이루어진 리스트를 반환한다. 그러면 템플릿에서 각 튜플의 값을 인덱스로 접근하면된다. 

따라서 3-2-1 사진에 있는 빨간색 네모 안에 있는 코드를 지우니 해결되었다.

3-2-1 오류 코드
3-2-2 오류
3-2-3 오류 해결