Web

종합반 플러스 - 2주차 - 나만의사전

또롱또 2022. 2. 27. 13:21
728x90

페이지 2개인 웹사이트 서버 작업

서버

from flask import Flask, render_template, request, jsonify, redirect, url_for
from pymongo import MongoClient
import requests

# 플라스크 웹앱 설정
app = Flask(__name__)

# Mongo DB에 연결
client = MongoClient('13.124.196.127', 27017, username="test", password="test")
db = client.dbsparta_plus_week2


@app.route('/')
def main():
    # 없는 단어 검색시, detail에서 보내온 msg를 html로 넘겨 주기 위한 함수.
    msg = request.args.get("msg")
    # DB에서 저장된 단어 찾아서 HTML에 나타내기
    words = list(db.words.find({}, {"_id": False}))
    return render_template("index.html", words=words, msg=msg)


@app.route('/detail/<keyword>')
def detail(keyword):
    # 단어의 old, new를 구별하기위해 주소창 뒤에 status_receive=old / status_receive=new 로 구별할거임
    status_receive = request.args.get("status_give")
    # API에서 단어 뜻 찾아서 결과 보내기
    r = requests.get(f"https://owlbot.info/api/v4/dictionary/{keyword}",
                     headers={"Authorization": "Token d214ec1596fec359127a5d690ac281f1c723dc4d"})
    # 만약 받아온 값의 status 코드가 200이 아닐경우 = 무언가가 잘못된 경우이다, key가 틀리던, 단어가 없던.
    if r.status_code != 200:
        # main함수가 있는곳으로 돌려준다, 뒤에 msg는 필수는 아니지만 main으로 보내줄 수 있다.
        return redirect(url_for("main", msg="the word is not exist in the world"))
    result = r.json()
    print(result)
    return render_template("detail.html", word=keyword, result=result, status=status_receive)


@app.route('/api/save_word', methods=['POST'])
def save_word():
    # 단어 저장하기
    word_receive = request.form["word_give"]
    definition_receive = request.form["definition_give"]

    # DB에 저장하기
    doc = {
        "word": word_receive,
        "definition": definition_receive
    }
    db.words.insert_one(doc)
    return jsonify({'result': 'success', 'msg': f'단어 {word_receive} 저장'})


@app.route('/api/delete_word', methods=['POST'])
def delete_word():
    # 단어 삭제하기
    word_receive = request.form["word_give"]
    db.words.delete_one({"word": word_receive})
    return jsonify({'result': 'success', 'msg': f'단어 {word_receive}삭제'})

# # # # # # # # # # # # 숙제, 나만의 예문 등록하기# # # # # # # # # # # # # #
@app.route('/api/get_exs', methods=['GET'])
def get_exs():
    # 예문 가져오기
    word_receive = request.args.get("word_give")
    result = list(db.examples.find({"word":word_receive},{"_id": False}))
    return jsonify({'result': 'success', "examples": result})

@app.route('/api/save_ex', methods=['POST'])
def save_ex():
    # 예문 저장하기
    word_receive = request.form["word_give"]
    example_receive = request.form["example_give"]

    # DB에 저장하기
    doc = {
        "word": word_receive,
        "example": example_receive,
    }
    db.examples.insert_one(doc)

    return jsonify({'result': 'Your Example is Added'})


@app.route('/api/delete_ex', methods=['POST'])
def delete_ex():
    # 예문 삭제하기
    word_receive = request.form["word_give"]
    number_receive = int(request.form["number_give"])
    example = list(db.examples.find({"word":word_receive}))[number_receive]["example"]
    db.examples.delete_one({"word": word_receive, "example": example})
    return jsonify({'result': 'Your Example is Deleted'})
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

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

 

상세페이지(단어)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Sparta Vocabulary Notebook</title>

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
          crossorigin="anonymous">

    <!-- Font Awesome -->
    <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

    <!-- CSS -->
    <link href='{{ url_for("static", filename="mystyle.css") }}' rel="stylesheet">

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
            integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
            crossorigin="anonymous"></script>

    <style>
        .container {
            width: 80%;
            max-width: 800px;
            margin: 30px auto;
            padding: 20px;
            background-color: white;

            border: solid 1px gray;
            border-radius: 10px;
        }

        span.example {
            color: gray;
            font-size: 14px;
        }

        .btn-sparta {
            color: #fff;
            background-color: #e8344e;
            border-color: #e8344e;
        }

        .btn-outline-sparta {
            color: #e8344e;
            background-color: transparent;
            background-image: none;
            border-color: #e8344e;
        }
    </style>

    <script>
        let word = '{{ word }}'
        $(document).ready(function () {
            // get_definitions()
            // 있는 단어 일때만, 예문이 나온다.
            {% if status=="old" %}
                get_examples();
            {% endif %}
        })

        {# AJAX 로 서버와 연결 #}
        function get_definitions() {
            $.ajax({
                type: "GET",
                {# 주소에서 WORD 에 받아오는 단어로 찾는다#}
                url: `https://owlbot.info/api/v4/dictionary/${word}`,
                beforeSend: function (xhr) {
                    {# TOKEN 뒤는 내 KEY #}
                    xhr.setRequestHeader("Authorization", "Token d214ec1596fec359127a5d690ac281f1c723dc4d");
                },
                data: {},
                error: function (xhr, status, error) {
                    alert("에러 발생!");
                },
                success: function (response) {
                    console.log(response)
                    {# 받아온 단어를 보여줌#}
                    $('#word').text(response["word"]);

                    {# 받아온 단어의 Pronunciation 이 null 이면 삭제, 아니면 보여줌#}
                    if (response["pronunciation"] == null) {
                        $('#pronunciation').text("");
                    } else {
                        $('#pronunciation').text(`/${response["pronunciation"]}/`)

                    }
                    {# DEFINITION 은 list 로 되어있기 때문에 for loop으로 풀어준다#}
                    let definitions = response["definitions"]
                    $('#definitions').empty();
                    for (let i = 0; i < definitions.length; ++i) {
                        let definition = definitions[i]

                        {# DEFINITION 의 Example 이 null 일 경우는 삭제, 아닐경우는 보여줌#}
                        let html_temp = "";
                        if (definition["example"] == null) {
                            html_temp = `<div style="padding:10px">
                                            <i>${definition["type"]}</i>
                                            <br>${definition["definition"]}<br>
                                        </div>`
                        } else {
                            html_temp = `<div style="padding:10px">
                                            <i>${definition["type"]}</i>
                                            <br>${definition["definition"]}<br>
                                            <span class="example">${definition["example"]}</span>
                                        </div>`
                        }
                        {# DEFINITION 을 HTML로 보여줌 #}
                        $('#definitions').append(html_temp);
                    }
                }
            })
        }

        function save_word() {
            $.ajax({
                type: "POST",
                url: `/api/save_word`,
                data: {
                    word_give: "{{ result.word }}",
                    definition_give: "{{ result.definitions[0].definition }}"
                },
                success: function (response) {
                    alert(response["msg"])
                    window.location.href = "/detail/{{ result.word }}?status_give=old"
                }
            });
        }

        function delete_word() {
            $.ajax({
                type: "POST",
                url: `/api/delete_word`,
                data: {
                    word_give: "{{ result.word }}"
                },
                success: function (response) {
                    alert(response["msg"])
                    window.location.href = "/"
                }
            });
        }

        function get_examples() {
            $("#example-list").empty()
            $.ajax({
                type: "GET",
                url: `/api/get_exs?word_give=${word}`,
                data: {},
                success: function (response) {
                    console.log(response)
                    let examples = response["examples"]
                    for(let i=0; i<examples.length; ++i){
                        let example = examples[i];
                        let html_temp = ` <li id="ex-${i}">${example["example"]}&nbsp;&nbsp;&nbsp;<a
                                             href="javascript:delete_ex(${i})">delete</a></li>`
                        $('#example-list').append(html_temp);
                    }
                }
            });
        }

        function add_ex() {
            let new_ex = $('#new-example').val();
            console.log(new_ex)
            // 문장이 그 단어를 포함하지 않을경우는, 포함하라고 경고를 띄워준다, 둘 다 소문자로 만들어서 비교
            if(!new_ex.toLowerCase().includes(word.toLowerCase())){
                alert("The Word is not included")
                window.location.reload();
                return;
            }

            $.ajax({
                type: "POST",
                url: `/api/save_ex`,
                data: {
                    word_give: word,
                    example_give: new_ex,
                },
                success: function (response) {
                    //console.log(response)
                    alert(response["result"])
                    get_examples()

                    $('#new-example').val("");
                }
            });
        }

        function delete_ex(i) {
            console.log("deleting", i)
            $.ajax({
                type: "POST",
                url: `/api/delete_ex`,
                data: {
                    word_give: word,
                    number_give: i
                },
                success: function (response) {
                    alert(response["result"])
                    get_examples()
                }
            });
        }
    </script>

</head>
<body>
<div class="wrap">
    {# 배너 #}
    <div class="banner" onclick="window.location.href = '/'">
    </div>
    {# 단어,설명 #}
    <div class="container">
        <div class="d-flex justify-content-between align-items-end">
            <div>
                {# jinja 2로 서버에서 받아오기 #}
                {#  word 혹은 result["word"] 혹은 result.word 다 괜찮음 #}
                <h1 id="word" style="display: inline;">{{ result.word }}</h1>
                {% if result.pronunciation != None %}
                    <h5 id="pronunciation" style="display: inline;">/{{ result.pronunciation }}/</h5>
                {% endif %}
            </div>
            {% if status=="new" %}
                <button id="btn-save" class="btn btn-outline-sparta btn-lg" onclick="save_word()">
                    <i class="fa fa-floppy-o" aria-hidden="true"></i>
                </button>
            {% endif %}
            {% if status=="old" %}
                <button id="btn-delete" class="btn btn-sparta btn-lg" onclick="delete_word()">
                    <i class="fa fa-trash" aria-hidden="true"></i>
                </button>
            {% endif %}
        </div>
        <hr>
        <div id="definitions">
            {% for definition in result.definitions %}
                <div style="padding:10px">
                    <i>{{ definition.type.encode('ascii','ignore').decode('utf-8')|safe }}</i>
                    <br>{{ definition.definition.encode('ascii','ignore').decode('utf-8')|safe }}<br>
                    {% if definition.example %}
                        {#  |safe : 앞에 들어오는건 안전한거니까 그냥 html이 들어오더라도 그대로 써달라는 명령     #}
                        {# encode('ascii','ignore').decode('utf-8') - 앞의 문자열의 아스키코드로 바꿀수없는건 무시해라, 바꿀수있는건 문자열로 바꿔줌 #}
                        <span class="example">{{ definition.example.encode('ascii','ignore').decode('utf-8')|safe }}</span>
                    {% endif %}
                </div>
            {% endfor %}
        </div>
    </div>
    {# 나만의 예문 #}
    {% if status=="old" %}
        <div id="examples" class="container">
        <h3 style="text-align: center;margin-bottom:1rem">Write your own sentences!</h3>
        <ul id="example-list">
            <li id="ex-0">This sentence contains the word 'word'.&nbsp;&nbsp;&nbsp;<a
                    href="javascript:delete_ex(0)">delete</a></li>
            <li id="ex-1">I don't like using the MS Word program.&nbsp;&nbsp;&nbsp;<a
                    href="javascript:delete_ex(1)">delete</a></li>
        </ul>
        <div class="d-flex justify-content-between" style="margin-left:20px;">
            <input id="new-example" class="form-control form-control-sm" style="margin-right: 0.5rem">
            <button class="btn btn-outline-secondary btn-sm" onclick="add_ex()">add</button>
        </div>
    </div>
    {% endif %}
</div>
</body>
</html>

메인페이지(목록)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Sparta Vocabulary Notebook</title>

    {# Favicon, og    #}
    <meta property="og:title" content="Sparta Vocabulary Notebook"/>
    <meta property="og:description" content="mini project for Web Plus"/>
    <meta property="og:image" content="{{ url_for('static', filename='logo_red.png') }}"/>
    <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
    <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
          crossorigin="anonymous">
    <!-- Font Awesome -->
    <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

    <!-- CSS -->
    <link href='{{ url_for("static", filename="mystyle.css") }}' rel="stylesheet">

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
            integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
            crossorigin="anonymous"></script>

    <style>
        .search-box {
            width: 70%;
            margin: 50px auto;
            max-width: 700px;
        }

        .table {
            width: 80%;
            max-width: 800px;
            margin: auto;
            table-layout: fixed;
        }

        .table th {
            border-top-style: none;
        }

        td {
            background-color: white;
            text-overflow: ellipsis;
            overflow: hidden;
            white-space: nowrap;
        }

        td > a, a:visited, a:hover, a:active {
            color: black;
        }

        thead:first-child tr:first-child th:first-child {
            border-radius: 10px 0 0 0;
        }

        thead:first-child tr:first-child th:last-child {
            border-radius: 0 10px 0 0;
        }

        tbody:last-child tr:last-child td:first-child {
            border-radius: 0 0 0 10px;
        }

        tbody:last-child tr:last-child td:last-child {
            border-radius: 0 0 10px 0;
        }

        tr.highlight > td{
            background-color: #e8344e;
            color: #fff;
        }
        tr.highlight > td > a{
            color: #fff;
        }
    </style>

    <script>
        {#  없는단어 검색시 받아온 MSG 를 보여주기  #}
        {% if msg %}
            alert("{{ msg }}")
        {% endif %}

        {# 검색 기능#}
        let words = {{ words|tojson }};
        let word_list = [];
        for(let i=0; i<words.length; ++i){
            word_list.push(words[i]["word"]);
            console.log(word_list)
        }

        function find_word(){
            // 검색 입력값 받아오기. - 대문자로쓰던, 소문자로쓰던 무조건 소문자로 받아온다.
            let word = $("#input-word").val().toLowerCase();
            // 만약 아무거도 입력을 안하면 알림
            if(word == ""){
                alert("Please Type Something")
                return;
            }
            // 만약에 단어가 있을경우 = 하이라이트
            if(word_list.includes(word)){
                $(`#word-${word}`).addClass("highlight");
                // id값의 다른 형제들의 클래스를 없애준다.
                $(`#word-${word}`).siblings().removeClass("highlight");
                $(`#word-${word}`)[0].scrollIntoView();

            }else{ // 없을경우, 그 단어페이지로 이동
               window.location.href = `/detail/${word}?status_give=new`
            }


        }

    </script>

</head>
<body>
<div class="wrap">
    <div class="banner" onclick="window.location.href='/'"></div>
    <div class="search-box d-flex justify-content-center">
        <input id="input-word" class="form-control" style="margin-right: 0.5rem">
        <button class="btn btn-light" onclick="find_word()"><i class="fa fa-search"></i></button>
    </div>
    <table class="table">
        <thead class="thead-light">
        <tr>
            <th scope="col" style="width:30%">WORD</th>
            <th scope="col">MEANING</th>

        </tr>
        </thead>
        <tbody id="tbody-box">
        {% for word in words %}
            <tr id="word-{{ word.word }}">
                <td><a href="/detail/{{ word.word }}?status_give=old">{{ word.word }}</a></td>
                <td>
                    {{ word.definition }}
                </td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
</div>

</body>
</html>

두 html 페이지가 똑같이 쓰는 css는 따로 빼주기

.wrap {
    background-color: RGBA(232, 52, 78, 0.2);
    min-height: 100vh;
    padding-bottom: 50px;
}

.banner {
    width: 100%;
    height: 200px;

    background-color: white;
    background-image: url('logo_red.png');

    background-position: center;
    background-size: contain;
    background-repeat: no-repeat;

    cursor: pointer;
}
728x90