그저 내가 되었고
항해99) 1주차:: 웹개발+ 2주차; SSR(Server Side Rendering) WORDBOOK 전체 코드 본문
개발/항해99 9기
항해99) 1주차:: 웹개발+ 2주차; SSR(Server Side Rendering) WORDBOOK 전체 코드
hyuunii 2022. 9. 21. 11:47- app.py
from flask import Flask, render_template, request, jsonify, redirect, url_for
from pymongo import MongoClient
import requests
app = Flask(__name__)
client = MongoClient('mongodb://아이디:비밀번호@내 아이피', 27017)
db = client.dbsparta_plus_week2
@app.route('/')
def main():
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):
# API에서 단어 뜻 찾아서 결과 보내기
status_receive = request.args.get("status_give", "old")
r = requests.get(f"https://owlbot.info/api/v4/dictionary/{keyword}",
headers={"Authorization": "Token [내 토큰]"})
if r.status_code != 200:
return redirect(url_for("main", msg="Word not found in dictionary; Try another word"))
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']
doc = {"word": word_receive, "definition": definition_receive}
db.words.insert_one(doc)
return jsonify({'result': 'success', 'msg': f'word "{word_receive}" saved'})
@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 "{word_receive}" deleted'})
if __name__ == '__main__':
app.run('0.0.0.0', port=5000, debug=True)
- index.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>
<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">
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<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;
}
tr.highlight > td {
background-color: #e8344e;
color: white;
}
tr.highlight a {
color: white;
}
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;
}
</style>
<script>
{% 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"])
}
function find_word() {
let word = $("#input-word").val().toLowerCase();
if (word == "") {
alert("please write something first :)")
return
}
if (word_list.includes(word)) {
$(`#word-${word}`).addClass('highlight').siblings().removeClass('highlight');
$(`#word-${word}`).get(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|safe }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>
- detail.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>
<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">
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<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>
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 () {
})
function save_word() {
$.ajax({
type: "POST",
url: `/api/save_word`,
data: {
word_give: "{{ word }}",
definition_give: "{{ result.definitions[0].definition }}"
},
success: function (response) {
alert(response["msg"])
window.location.href = "/detail/{{ word }}?status=old"
}
});
}
function delete_word() {
$.ajax({
type: "POST",
url: `/api/delete_word`,
data: {
word_give: '{{ word }}',
},
success: function (response) {
alert(response["msg"])
window.location.href = "/"
}
});
}
</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>
<h1 id="word" style="display: inline;">{{ result.word }}</h1>
{% if result.pronunciation %}
<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>
{% else %}
<button id="btn-delete" class="btn btn-sparta btn-lg" onclick="delete_word()"><i
class="fa fa-trash-o"
aria-hidden="true"></i></button>
{% endif %}
</div>
<hr>
<div id="definitions">
{% set definitions = result.definitions %}
{% for definition in definitions %}
<div style="padding:10px">
<i>{{ definition.type }}</i>
<br>{{ definition.definition.encode('ascii', 'ignore').decode('utf-8') }}<br>
{% if definition.example %}
<span class="example">{{ definition.example.encode('ascii', 'ignore').decode('utf-8') }}</span>
{% endif %}
</div>
{% endfor %}
</div>
</div>
</div>
</body>
</html>
- mystyle.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;
}
.container {
width: 80%;
max-width: 800px;
margin: 30px auto;
padding: 20px;
background-color: white;
border: solid 1px gray;
border-radius: 10px;
}
'개발 > 항해99 9기' 카테고리의 다른 글
항해99) 1주차:: 미니프로젝트 회고(p.s. 끝나지 않는 CSS 집착.....) (0) | 2022.09.23 |
---|---|
항해99) 1주차:: 웹개발+ 3주차; Selenium으로 스크래핑 (0) | 2022.09.21 |
항해99) 1주차:: 웹개발+ 2주차; SSR(Server Side Rendering) (0) | 2022.09.21 |
항해99) 1주차:: 웹개발+ 1주차; 파일 업로드 (0) | 2022.09.19 |
📚지금까지 헷갈리는 것 정리 (0) | 2022.09.09 |