Improved several things, especially /modapp

This commit is contained in:
2024-10-13 21:27:31 +01:00
committed by GitHub
parent 1ad0569ed5
commit 35c90e0210

99
app.py
View File

@@ -9,6 +9,7 @@ import random
from argon2 import PasswordHasher from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError from argon2.exceptions import VerifyMismatchError
from werkzeug.middleware.proxy_fix import ProxyFix # Import ProxyFix from werkzeug.middleware.proxy_fix import ProxyFix # Import ProxyFix
import logging
app = Flask(__name__) app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///quotes.db' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///quotes.db'
@@ -21,6 +22,9 @@ app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1, x_
# Initialize Argon2 password hasher # Initialize Argon2 password hasher
ph = PasswordHasher() ph = PasswordHasher()
# Initialize logging for debugging
logging.basicConfig(level=logging.DEBUG)
# Hardcoded admin credentials (hashed password using Argon2) # Hardcoded admin credentials (hashed password using Argon2)
ADMIN_CREDENTIALS = { ADMIN_CREDENTIALS = {
'username': 'admin', 'username': 'admin',
@@ -92,7 +96,7 @@ def vote(id, action):
else: else:
vote_data = {} vote_data = {}
# If user already voted, handle undo or switch vote # If the user has already voted, check for undoing or switching vote
if str(id) in vote_data: if str(id) in vote_data:
previous_action = vote_data[str(id)] previous_action = vote_data[str(id)]
@@ -113,12 +117,12 @@ def vote(id, action):
vote_data[str(id)] = action # Update vote action vote_data[str(id)] = action # Update vote action
flash("Your vote has been changed.", 'success') flash("Your vote has been changed.", 'success')
else: else:
# First time voting on this quote # First-time voting on this quote (starting from neutral)
if action == 'upvote': if action == 'upvote':
quote.votes += 1 quote.votes += 1 # Add +1 vote
elif action == 'downvote': elif action == 'downvote':
quote.votes -= 1 quote.votes -= 1 # Subtract -1 vote
vote_data[str(id)] = action # Store vote vote_data[str(id)] = action # Store vote action
flash("Thank you for voting!", 'success') flash("Thank you for voting!", 'success')
# Save the updated vote data to the cookie # Save the updated vote data to the cookie
@@ -136,15 +140,19 @@ def vote(id, action):
# Route for displaying a random quote # Route for displaying a random quote
@app.route('/random') @app.route('/random')
def random_quote(): def random_quote():
count = Quote.query.filter_by(status=1).count() # Only count approved quotes approved_count = Quote.query.filter_by(status=1).count()
pending_count = Quote.query.filter_by(status=0).count()
count = Quote.query.count()
if count == 0: if count == 0:
flash("No approved quotes available yet.", 'error') flash("No quotes available yet.", 'error')
return redirect(url_for('index')) return redirect(url_for('index'))
random_offset = random.randint(0, count - 1) # Generate a random offset random_id = random.randint(1, count)
random_quote = Quote.query.filter_by(status=1).offset(random_offset).first() # Fetch a random approved quote random_quote = Quote.query.get(random_id)
return render_template('random.html', quote=random_quote, approved_count=approved_count, pending_count=pending_count)
return render_template('random.html', quote=random_quote)
@app.route('/<int:id>') @app.route('/<int:id>')
def quote_homepathid(id): def quote_homepathid(id):
@@ -193,54 +201,47 @@ def modapp():
flash('You need to log in first.', 'danger') flash('You need to log in first.', 'danger')
return redirect(url_for('login')) return redirect(url_for('login'))
all_quotes = Quote.query.order_by(Quote.date.desc()).all() # Apply filtering (pending, approved, rejected)
total_quotes = Quote.query.filter_by(status=1).count() # Count only approved quotes filter_status = request.args.get('filter', 'pending')
page = request.args.get('page', 1, type=int)
return render_template('modapp.html', all_quotes=all_quotes, total_quotes=total_quotes) if filter_status == 'approved':
quotes = Quote.query.filter_by(status=1).order_by(Quote.date.desc()).paginate(page=page, per_page=10)
elif filter_status == 'rejected':
quotes = Quote.query.filter_by(status=2).order_by(Quote.date.desc()).paginate(page=page, per_page=10)
else: # Default to pending
quotes = Quote.query.filter_by(status=0).order_by(Quote.date.desc()).paginate(page=page, per_page=10)
@app.route('/modapp/bulk_action', methods=['POST']) # Get counts for each status
def bulk_action(): approved_count = Quote.query.filter_by(status=1).count()
action = request.form.get('action') pending_count = Quote.query.filter_by(status=0).count()
quote_ids = request.form.getlist('quote_ids') rejected_count = Quote.query.filter_by(status=2).count()
if not quote_ids: return render_template('modapp.html', quotes=quotes, filter_status=filter_status,
flash("No quotes selected.", "warning") approved_count=approved_count, pending_count=pending_count,
return redirect(url_for('modapp')) rejected_count=rejected_count)
valid_actions = ['approve', 'reject', 'delete']
if action not in valid_actions:
flash("Invalid action selected.", "error")
return redirect(url_for('modapp'))
if action == 'approve': # Helper function to approve a quote
for quote_id in quote_ids:
approve_quote(quote_id)
elif action == 'reject':
for quote_id in quote_ids:
reject_quote(quote_id)
elif action == 'delete':
for quote_id in quote_ids:
delete_quote(quote_id)
flash(f"Bulk action '{action}' applied to selected quotes.", "success")
return redirect(url_for('modapp'))
# Define helper functions for each action
def approve_quote(quote_id): def approve_quote(quote_id):
quote = Quote.query.get(quote_id) quote = Quote.query.get(quote_id)
if quote: if quote and quote.status != 1: # Only approve if not already approved
quote.status = 1 # Approved quote.status = 1 # Approved
db.session.commit() db.session.commit()
# Helper function to reject a quote
def reject_quote(quote_id): def reject_quote(quote_id):
quote = Quote.query.get(quote_id) quote = Quote.query.get(quote_id)
if quote: if quote and quote.status != 2: # Only reject if not already rejected
logging.debug(f"Rejecting quote ID: {quote.id}") # Add logging for rejection
quote.status = 2 # Rejected quote.status = 2 # Rejected
db.session.commit() db.session.commit()
# Helper function to delete a quote
def delete_quote(quote_id): def delete_quote(quote_id):
quote = Quote.query.get(quote_id) quote = Quote.query.get(quote_id)
if quote: if quote:
logging.debug(f"Deleting quote ID: {quote.id}") # Add logging for deletion
db.session.delete(quote) db.session.delete(quote)
db.session.commit() db.session.commit()
@@ -249,19 +250,31 @@ def search():
query = request.args.get('q', '').strip() # Get the search query and trim whitespace query = request.args.get('q', '').strip() # Get the search query and trim whitespace
quotes = [] quotes = []
# Query the counts of approved and pending quotes
approved_count = Quote.query.filter_by(status=1).count()
pending_count = Quote.query.filter_by(status=0).count()
if query: if query:
# Perform the search only if the query is provided # Perform the search only if the query is provided
quotes = Quote.query.filter(Quote.text.like(f'%{query}%'), Quote.status == 1).all() quotes = Quote.query.filter(Quote.text.like(f'%{query}%'), Quote.status == 1).all()
# Render the search page with the results (or empty if no query) # Render the search page with the results, counts, and search query
return render_template('search.html', quotes=quotes, query=query) return render_template('search.html', quotes=quotes, query=query, approved_count=approved_count, pending_count=pending_count)
# Route for browsing approved quotes # Route for browsing approved quotes
@app.route('/browse', methods=['GET']) @app.route('/browse', methods=['GET'])
def browse(): def browse():
# Query the counts of approved and pending quotes
approved_count = Quote.query.filter_by(status=1).count()
pending_count = Quote.query.filter_by(status=0).count()
# Pagination setup
page = request.args.get('page', 1, type=int) page = request.args.get('page', 1, type=int)
quotes = Quote.query.filter_by(status=1).order_by(Quote.date.desc()).paginate(page=page, per_page=10) quotes = Quote.query.filter_by(status=1).order_by(Quote.date.desc()).paginate(page=page, per_page=10)
return render_template('browse.html', quotes=quotes)
# Pass the counts and the quotes to the template
return render_template('browse.html', quotes=quotes, approved_count=approved_count, pending_count=pending_count)
# Approve a quote (admin only) # Approve a quote (admin only)
@app.route('/approve/<int:id>') @app.route('/approve/<int:id>')