logic move out

This commit is contained in:
m
2026-03-17 09:14:50 +01:00
parent 43bec07510
commit f7feb18de3
4 changed files with 237 additions and 33 deletions

4
.gitignore vendored
View File

@@ -2,4 +2,6 @@
docker-compose.yml
Dockerfile
Caddyfile
deploy.sh
deploy.sh
.vscode/
.ruff_cache/

86
app.py
View File

@@ -1,6 +1,6 @@
from flask import Flask, render_template, request, redirect, url_for
from content.posts import BLOG_POSTS
from content.logic import get_enriched_post, get_comments_for_post, save_comment
from flask_logic.logic import get_enriched_post, get_comments_for_post, save_comment
# 🌟 IMPORT THE content from separate files.
from content.posts import BLOG_POSTS
@@ -16,15 +16,6 @@ COMMENT_FILE = 'content/comments.csv'
MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024 # 5 Megabytes
POSTS_PER_PAGE = 5 # posts per page limit here
"""
import os
from flask import send_from_directory
@app.route('/content/images/<path:filename>')
def custom_static(filename):
# This serves files from your private submodule folder
return send_from_directory('content/images', filename)
"""
@app.route('/post/<int:post_id>/comment', methods=['POST'])
def post_comment(post_id):
@@ -43,35 +34,66 @@ def post_comment(post_id):
# --- Routes ---
def calculate_pagination(posts, posts_per_page, page):
"""
Calculate pagination parameters for blog posts.
Args:
posts (list): Full list of blog posts
posts_per_page (int): Number of posts per page
page (int): Current page number (1-indexed)
Returns:
dict: Pagination data including:
- posts_to_show: Posts for current page
- prev_url: Previous page URL or None
- next_url: Next page URL or None
- current_page: Current page number
- total_pages: Total number of pages
- total_posts: Total number of posts
"""
# Sort posts by ID newest first
sorted_posts = sorted(posts, key=lambda x: x['id'], reverse=True)
total_posts = len(sorted_posts)
total_pages = math.ceil(total_posts / posts_per_page)
# Clamp page to valid range
page = max(1, min(page, total_pages))
start = (page - 1) * posts_per_page
end = start + posts_per_page
posts_to_show = sorted_posts[start:end]
prev_url = url_for('home', page=page - 1) if page > 1 else None
next_url = url_for('home', page=page + 1) if end < total_posts else None
return {
'posts_to_show': posts_to_show,
'prev_url': prev_url,
'next_url': next_url,
'current_page': page,
'total_pages': total_pages,
'total_posts': total_posts
}
@app.route('/')
def home():
"""Home page with paginated blog posts."""
page = request.args.get('page', 1, type=int)
blog_title = TITLE
# 2. Sort posts by ID newest first
all_posts = sorted(BLOG_POSTS, key=lambda x: x['id'], reverse=True)
# Calculate totals
total_posts = len(all_posts)
total_pages = math.ceil(total_posts / POSTS_PER_PAGE)
# 3. Calculate start and end indices
start = (page - 1) * POSTS_PER_PAGE
end = start + POSTS_PER_PAGE
# 4. Slice the list for the current page
posts_to_show = all_posts[start:end]
# 5. Determine if there is a next or previous page
prev_url = url_for('home', page=page - 1) if page > 1 else None
next_url = url_for('home', page=page + 1) if end < len(all_posts) else None
pagination = calculate_pagination(BLOG_POSTS, POSTS_PER_PAGE, page)
return render_template('index.html',
posts=posts_to_show,
prev_url=prev_url,
next_url=next_url,
current_page=page,
blog_title=blog_title,
total_pages=total_pages)
posts=pagination['posts_to_show'],
prev_url=pagination['prev_url'],
next_url=pagination['next_url'],
current_page=pagination['current_page'],
total_pages=pagination['total_pages'],
blog_title=blog_title,
total_posts=pagination['total_posts'])

93
flask_logic.py/logic.py Normal file
View File

@@ -0,0 +1,93 @@
import os
import csv
COMMENT_FILE = os.path.join(os.path.dirname(__file__), '../content/comments.csv')
MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024 # 5MB example
import datetime
def save_comment(post_id, author, content):
"""Handles the validation and saving of comments to the private CSV."""
# 1. Validation logic moved here
if not content or len(content) > 1000:
return False
author = (author or "Anonymous").strip()[:50]
content = content.strip()
# 2. Path & Size Checks
file_exists = os.path.isfile(COMMENT_FILE)
if file_exists and os.path.getsize(COMMENT_FILE) > MAX_FILE_SIZE_BYTES:
return False # Or raise a specific error
# 3. Write to CSV
with open(COMMENT_FILE, 'a', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['post_id', 'author', 'content', 'date'])
if not file_exists:
writer.writeheader()
writer.writerow({
'post_id': post_id,
'author': author,
'content': content,
'date': datetime.datetime.now().strftime("%B %d, %Y at %I:%M %p")
})
return True
def load_snippet(filename):
"""Helper to read HTML snippets from the data folder."""
base_path = os.path.join(os.path.dirname(__file__), 'data')
file_path = os.path.join(base_path, filename)
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
return ""
def get_enriched_post(post_id, BLOG_POSTS):
post = next((p for p in BLOG_POSTS if p.get('id') == post_id), None)
if not post:
return None
# Mapping config: keeps logic.py tiny
configs = {
10: {
"template": "components/timeline.html",
"timeline_file": "post_10_timeline.html"
},
8: {
"template": "components/christmas_post.html",
"timeline": None
},
}
if post_id in configs:
conf = configs[post_id]
post_template = conf.get("template")
if post_template:
post["template"] = post_template
# Only load the file if a filename is provided
t_file = conf.get("timeline_file")
if t_file:
post["timeline"] = load_snippet(t_file)
return post
def get_comments_for_post(post_id):
comments = []
if not os.path.exists(COMMENT_FILE):
return comments
if os.path.getsize(COMMENT_FILE) > MAX_FILE_SIZE_BYTES:
return ["Comment section full."] # Handle error in app.py
with open(COMMENT_FILE, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
if row['post_id'] == str(post_id):
comments.append(row)
return comments

87
readme.md Normal file
View File

@@ -0,0 +1,87 @@
# Flask Blog Templates
A minimal **Flask template set** for my blog.
This project adapts and evolves the original [simple-blog-template](https://github.com/earlbread/simple-blog-template) by [Seunghun Lee](https://github.com/earlbread).
## Git Tag
Current version:
```bash
git tag -a v1.0 -m "Initial release of Flask Blog Templates"
git push origin v1.0
```
---
## Overview
It provides a growing collection of styling templates and is designed to run with two additional files (see below).
In the current setup, blog content is defined using Python dictionaries (example below), while comments are stored in a CSV file at `content/comments.csv`.
A basic blog structure is provided by the existing templates. Additional per-article styling can be applied either by embedding raw HTML directly in the post content or by using template expansion via Jinja:
```jinja2
{% if post.template %}
{% include post.template %}
{% endif %}
```
For example, `templates/components/timeline.html` demonstrates a timeline component.
An example data structure is provided below.
---
## Required Files
This two files should be included in program folder to run the app.
### `content/about.py`
provides text for about page and blog title
```python
ABOUT = {
"content": """
<p>Hello here is the about page</p>
"""
}
TITLE = {
"title": "Hello World"
}
```
### `content/posts.py`
provides articles in blog:
```python
BLOG_POSTS = [
{
'id': 7,
'title': "title",
'subtitle': "subtitle",
'date': "December 10, 2024",
'content': """can use raw html string for style, will be rendered as html
""",
'displayall': False
},
]
```
## Template Evolution
- Originally based on the Simple Blog Template project.
- Adapted for Jinja2 rendering (Flask compatibility).
- CSS rewritten for flexibility and modular styling.
- Template design will continue accumulating for themes and post customization.
## License
- This project is distributed under the [MIT License](https://github.com/earlbread/simple-blog-template/blob/master/LICENSE).