-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
185 lines (146 loc) · 5.05 KB
/
app.py
File metadata and controls
185 lines (146 loc) · 5.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# app.py
"""Minimal Flask blog with file-based JSON storage.
Features:
- Display all posts
- Add / Update / Delete posts
- Like posts
- JSON API endpoints
Run:
pip install flask
python app.py
"""
from __future__ import annotations
from typing import Any, Dict
from flask import (
Flask,
abort,
jsonify,
redirect,
render_template,
request,
url_for,
)
import storage # JSON storage helper for posts.json
app = Flask(__name__)
# ---------------------------- Pages -----------------------------------------
@app.get("/")
def index():
"""Render the index page with all posts."""
posts = storage.load_posts()
return render_template("index.html", posts=posts)
@app.route("/add", methods=["GET", "POST"])
def add():
"""Render and handle the add-post form."""
if request.method == "POST":
author = (request.form.get("author") or "").strip()
title = (request.form.get("title") or "").strip()
content = (request.form.get("content") or "").strip()
if not author or not title or not content:
return (
render_template(
"add.html",
error="All fields are required.",
form={
"author": author,
"title": title,
"content": content,
},
),
400,
)
storage.add_post(author, title, content)
return redirect(url_for("index"))
return render_template("add.html")
@app.route("/update/<int:post_id>", methods=["GET", "POST"])
def update(post_id: int):
"""Render and handle the update-post form."""
post = storage.get_post(post_id)
if not post:
abort(404, description="Post not found")
if request.method == "POST":
author = (request.form.get("author") or "").strip()
title = (request.form.get("title") or "").strip()
content = (request.form.get("content") or "").strip()
if not author or not title or not content:
# Re-render with the values the user just submitted
return (
render_template(
"update.html",
error="All fields are required.",
post={
"id": post_id,
"author": author,
"title": title,
"content": content,
},
),
400,
)
storage.update_post(post_id, author=author, title=title, content=content)
return redirect(url_for("index"))
# GET -> show form prefilled with current post
return render_template("update.html", post=post)
@app.route("/delete/<int:post_id>")
def delete(post_id: int):
"""Delete a post by ID and redirect to index."""
ok = storage.delete_post(post_id)
if not ok:
abort(404, description="Post not found")
return redirect(url_for("index"))
@app.route("/like/<int:post_id>")
def like(post_id: int):
"""Increment like counter for a post and redirect to index."""
updated = storage.like_post(post_id)
if not updated:
abort(404, description="Post not found")
return redirect(url_for("index"))
# ----------------------------- JSON API -------------------------------------
@app.get("/health")
def health():
"""Lightweight health endpoint."""
return jsonify(status="ok")
@app.get("/posts")
def list_posts():
"""Return all posts as JSON."""
return jsonify(storage.load_posts())
@app.get("/posts/<int:post_id>")
def get_post(post_id: int):
"""Return a single post as JSON or 404."""
post = storage.get_post(post_id)
if not post:
abort(404, description="Post not found")
return jsonify(post)
@app.post("/posts")
def create_post():
"""Create a post from JSON body."""
data: Dict[str, Any] = request.get_json(silent=True) or {}
author = data.get("author")
title = data.get("title")
content = data.get("content")
if not all([author, title, content]):
abort(400, description="author, title, and content are required")
new_post = storage.add_post(str(author), str(title), str(content))
return jsonify(new_post), 201
@app.patch("/posts/<int:post_id>")
def patch_post(post_id: int):
"""Partially update a post by ID."""
data: Dict[str, Any] = request.get_json(silent=True) or {}
updated = storage.update_post(
post_id,
author=data.get("author"),
title=data.get("title"),
content=data.get("content"),
)
if not updated:
abort(404, description="Post not found")
return jsonify(updated)
@app.delete("/posts/<int:post_id>")
def remove_post(post_id: int):
"""Delete a post by ID via API."""
ok = storage.delete_post(post_id)
if not ok:
abort(404, description="Post not found")
return jsonify({"deleted": post_id})
if __name__ == "__main__":
# Bind on all interfaces for local containers/dev; use a non-privileged port.
app.run(host="0.0.0.0", port=4999, debug=True)