ishaque123 commited on
Commit
b6ba5f6
·
verified ·
1 Parent(s): c189743

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +227 -0
app.py ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import sqlite3
3
+ import os
4
+ from datetime import datetime
5
+ from dotenv import load_dotenv
6
+ import gradio as gr
7
+ import markdown
8
+
9
+ # Optional: load .env (not required unless you use secrets)
10
+ load_dotenv()
11
+
12
+ DB_PATH = os.environ.get("BLOG_DB", "blog.db")
13
+
14
+ def get_conn():
15
+ conn = sqlite3.connect(DB_PATH, check_same_thread=False)
16
+ conn.row_factory = sqlite3.Row
17
+ return conn
18
+
19
+ def init_db():
20
+ conn = get_conn()
21
+ cur = conn.cursor()
22
+ cur.execute(
23
+ """
24
+ CREATE TABLE IF NOT EXISTS posts (
25
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
26
+ title TEXT NOT NULL,
27
+ body TEXT NOT NULL,
28
+ tags TEXT,
29
+ created_at TEXT NOT NULL,
30
+ updated_at TEXT NOT NULL
31
+ )
32
+ """
33
+ )
34
+ conn.commit()
35
+ conn.close()
36
+
37
+ init_db()
38
+
39
+ # ---------- DB helpers ----------
40
+ def create_post(title, body, tags=""):
41
+ now = datetime.utcnow().isoformat()
42
+ conn = get_conn()
43
+ cur = conn.cursor()
44
+ cur.execute(
45
+ "INSERT INTO posts (title, body, tags, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
46
+ (title, body, tags, now, now)
47
+ )
48
+ conn.commit()
49
+ post_id = cur.lastrowid
50
+ conn.close()
51
+ return post_id
52
+
53
+ def update_post(post_id, title, body, tags=""):
54
+ now = datetime.utcnow().isoformat()
55
+ conn = get_conn()
56
+ cur = conn.cursor()
57
+ cur.execute(
58
+ "UPDATE posts SET title = ?, body = ?, tags = ?, updated_at = ? WHERE id = ?",
59
+ (title, body, tags, now, post_id)
60
+ )
61
+ conn.commit()
62
+ conn.close()
63
+
64
+ def delete_post(post_id):
65
+ conn = get_conn()
66
+ cur = conn.cursor()
67
+ cur.execute("DELETE FROM posts WHERE id = ?", (post_id,))
68
+ conn.commit()
69
+ conn.close()
70
+
71
+ def get_post(post_id):
72
+ conn = get_conn()
73
+ cur = conn.cursor()
74
+ cur.execute("SELECT * FROM posts WHERE id = ?", (post_id,))
75
+ row = cur.fetchone()
76
+ conn.close()
77
+ return dict(row) if row else None
78
+
79
+ def list_posts():
80
+ conn = get_conn()
81
+ cur = conn.cursor()
82
+ cur.execute("SELECT id, title, tags, created_at, updated_at FROM posts ORDER BY created_at DESC")
83
+ rows = cur.fetchall()
84
+ conn.close()
85
+ return [dict(r) for r in rows]
86
+
87
+ # ---------- Gradio app logic ----------
88
+ def new_post(title, body, tags):
89
+ if not title or not body:
90
+ return "Title and Body are required.", gr.update(value="", interactive=True)
91
+ post_id = create_post(title, body, tags or "")
92
+ return f"Post created (ID: {post_id})", gr.update(value="") # Clear inputs
93
+
94
+ def edit_post_load(post_id_str):
95
+ if not post_id_str:
96
+ return "Please select a post ID.", "", "", ""
97
+ try:
98
+ pid = int(post_id_str)
99
+ except ValueError:
100
+ return "Invalid post ID.", "", "", ""
101
+ post = get_post(pid)
102
+ if not post:
103
+ return f"No post with ID {pid}.", "", "", ""
104
+ return f"Loaded post {pid} for editing.", post["title"], post["body"], post["tags"] or ""
105
+
106
+ def save_edited(post_id_str, title, body, tags):
107
+ try:
108
+ pid = int(post_id_str)
109
+ except Exception:
110
+ return "Invalid post ID."
111
+ if not get_post(pid):
112
+ return f"No post with ID {pid}."
113
+ update_post(pid, title, body, tags or "")
114
+ return f"Post {pid} updated."
115
+
116
+ def remove_post(post_id_str):
117
+ try:
118
+ pid = int(post_id_str)
119
+ except Exception:
120
+ return "Invalid post ID."
121
+ if not get_post(pid):
122
+ return f"No post with ID {pid}."
123
+ delete_post(pid)
124
+ return f"Post {pid} deleted."
125
+
126
+ def render_post_preview(title, body):
127
+ # Convert markdown to HTML (safe for simple uses; for production use a sanitizer)
128
+ html = markdown.markdown(body or "")
129
+ header = f"<h2>{title or ''}</h2><hr>"
130
+ return header + html
131
+
132
+ def export_markdown(post_id_str):
133
+ try:
134
+ pid = int(post_id_str)
135
+ except Exception:
136
+ return None, "Invalid post ID."
137
+ post = get_post(pid)
138
+ if not post:
139
+ return None, f"No post with ID {pid}."
140
+ md = f"# {post['title']}\n\n{post['body']}\n\n*Tags: {post['tags'] or ''}*\n\n_Created: {post['created_at']}_\n"
141
+ filename = f"post_{pid}.md"
142
+ path = os.path.join("/tmp", filename) if os.path.isdir("/tmp") else filename
143
+ with open(path, "w", encoding="utf-8") as f:
144
+ f.write(md)
145
+ return path, f"Exported to {path}"
146
+
147
+ # ---------- UI ----------
148
+ with gr.Blocks(title="Simple Blog Writer") as demo:
149
+ gr.Markdown("# ✍️ Simple Blog Writer (Gradio + SQLite)\nCreate, edit, view, and export posts.")
150
+ with gr.Tabs():
151
+ with gr.TabItem("Create Post"):
152
+ title_in = gr.Textbox(label="Title")
153
+ tags_in = gr.Textbox(label="Tags (comma separated)")
154
+ body_in = gr.Textbox(label="Body (Markdown)", lines=12)
155
+ create_btn = gr.Button("Create Post")
156
+ create_out = gr.Textbox(label="Status", interactive=False)
157
+ create_btn.click(new_post, inputs=[title_in, body_in, tags_in], outputs=[create_out, body_in])
158
+
159
+ with gr.TabItem("List Posts"):
160
+ posts_list = gr.Dataframe(headers=["id", "title", "tags", "created_at", "updated_at"], interactive=False)
161
+ refresh_btn = gr.Button("Refresh List")
162
+ refresh_msg = gr.Textbox(label="Message", interactive=False)
163
+
164
+ def refresh_table():
165
+ rows = list_posts()
166
+ table = [[r["id"], r["title"], r["tags"], r["created_at"], r["updated_at"]] for r in rows]
167
+ return table, f"Found {len(table)} posts."
168
+ refresh_btn.click(refresh_table, outputs=[posts_list, refresh_msg])
169
+
170
+ with gr.TabItem("Edit Post"):
171
+ edit_id = gr.Textbox(label="Post ID to load")
172
+ load_btn = gr.Button("Load for Edit")
173
+ load_status = gr.Textbox(label="Load status", interactive=False)
174
+ edit_title = gr.Textbox(label="Title")
175
+ edit_tags = gr.Textbox(label="Tags")
176
+ edit_body = gr.Textbox(label="Body (Markdown)", lines=12)
177
+ save_btn = gr.Button("Save Changes")
178
+ save_status = gr.Textbox(label="Save status", interactive=False)
179
+
180
+ load_btn.click(edit_post_load, inputs=[edit_id], outputs=[load_status, edit_title, edit_body, edit_tags])
181
+ save_btn.click(save_edited, inputs=[edit_id, edit_title, edit_body, edit_tags], outputs=[save_status])
182
+
183
+ with gr.TabItem("View / Export"):
184
+ view_id = gr.Textbox(label="Post ID to view")
185
+ view_btn = gr.Button("View")
186
+ view_out = gr.HTML()
187
+ preview_btn = gr.Button("Preview (HTML)")
188
+ preview_out = gr.HTML()
189
+ export_btn = gr.Button("Export Markdown to file")
190
+ export_file = gr.File(label="Download exported file")
191
+ msg_box = gr.Textbox(label="Message", interactive=False)
192
+
193
+ def view_plain(post_id_str):
194
+ try:
195
+ pid = int(post_id_str)
196
+ except:
197
+ return "Invalid post ID.", ""
198
+ post = get_post(pid)
199
+ if not post:
200
+ return f"No post with ID {pid}.", ""
201
+ md = f"# {post['title']}\n\n{post['body']}\n\n*Tags: {post['tags'] or ''}*"
202
+ return f"Showing post {pid}:", md
203
+ view_btn.click(view_plain, inputs=[view_id], outputs=[msg_box, view_out])
204
+
205
+ def preview_html(post_id_str):
206
+ try:
207
+ pid = int(post_id_str)
208
+ except:
209
+ return "Invalid ID."
210
+ post = get_post(pid)
211
+ if not post:
212
+ return "No post found."
213
+ html = render_post_preview(post["title"], post["body"])
214
+ return html
215
+ preview_btn.click(preview_html, inputs=[view_id], outputs=[preview_out])
216
+
217
+ def export_file_click(post_id_str):
218
+ path, msg = export_markdown(post_id_str)
219
+ if not path:
220
+ return None, msg
221
+ return path, msg
222
+ export_btn.click(export_file_click, inputs=[view_id], outputs=[export_file, msg_box])
223
+
224
+ gr.Markdown("**Note:** This example has no authentication. Add login before using in production.")
225
+
226
+ if __name__ == "__main__":
227
+ demo.launch(server_name="0.0.0.0", share=False)