Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ Promptl is currently available at [promptl.com](https://promptl.com)!

# Todo
- [ ] Update the nav bar display on different devices
- [ ] Fix sign up logic
- [ ] ~~Fix sign up logic~~
- [x] ~~Save story~~
168 changes: 92 additions & 76 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from functools import wraps
from flask import Flask, request, session, redirect, url_for, render_template, flash
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import timedelta
# from bson import ObjectId
import bcrypt
# from datetime import datetime
Expand All @@ -16,10 +17,13 @@

# create the flask app and add configurations
app = Flask(__name__)
app.config['SECRET_KEY'] = "key"
app.config['SECRET_KEY'] = "your-more-secure-secret-key-here" # Use a more secure key in production
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=24) # Session expires after 24 hours

# generate the prompts for the main page
story_prompts = prompts.gen_all_prompts()
# Make sessions permanent so they persist across requests
@app.before_request
def make_session_permanent():
session.permanent = True

# create a login decorator to check if the user is logged in
def login_required(f):
Expand All @@ -39,8 +43,9 @@ def index():
@app.route('/home')
@login_required
def home():
# regenerate the prompts if the page is reloaded
# Generate new prompts and store them in session for later use in save_writing
story_prompts = prompts.gen_all_prompts()
session['current_prompts'] = story_prompts # Store prompts in session to persist across requests

# allocate each prompt to a variable
name = story_prompts['name']
Expand Down Expand Up @@ -100,18 +105,17 @@ def signup():
print(f"Database error checking user: {e}")
return render_template("signup.html", message="An error occurred. Please try again.")

# hash the password before storing
# Use plain password since db.add_user() handles hashing internally
try:
hashed_password = generate_password_hash(password)
print(f"Debug - Password hashed successfully for user: {username}") # Debug line
print(f"Debug - Creating user: {username}") # Debug line

except Exception as e:
print(f"Password hashing error: {e}")
print(f"Error preparing user data: {e}")
return render_template("signup.html", message="An error occurred. Please try again.")

# attempt to create the new user
try:
result = db.add_user(username, hashed_password)
result = db.add_user(username, password) # Pass plain password - db.add_user() will hash it

# Check if user creation was successful
if result is not None:
Expand All @@ -120,7 +124,7 @@ def signup():
return redirect(url_for('login'))

else:
# database operation failed - this is where your error is occurring
# database operation failed
return render_template("signup.html", message="Failed to create account. Please try again.")

except Exception as e:
Expand All @@ -143,51 +147,20 @@ def login():
if not username or not password:
return render_template("login.html", message="Please fill in all fields.")

# find the user in the database (if they exist)
user = db.get_user(username)
# Use authenticate_user function which handles password hashing verification
user = db.authenticate_user(username, password)

# if the user exists, check if the password is correct
if user != None:
# debugging
print(f"Debug - User found: {username}")
print(f"Debug - Password field exists: {'password' in user}")
if user:
print(f"Debug - Authentication successful for user: {username}")

# Check if it's a bcrypt hash (starts with $2b$) or werkzeug hash
stored_password = user['password']
password_correct = False
# store user id in session
session['user_id'] = str(user['id'])
session['username'] = username

if stored_password.startswith('$2b$') or stored_password.startswith('$2a$'):
# This is a bcrypt hash - need to check it with bcrypt
import bcrypt
password_correct = bcrypt.checkpw(password.encode('utf-8'), stored_password.encode('utf-8'))
print("Debug - Checking bcrypt password")
else:
# This is a werkzeug hash - use check_password_hash
try:
password_correct = check_password_hash(stored_password, password)
print("Debug - Checking werkzeug password")
except:
print("Debug - Password hash format not recognized")
password_correct = False

if password_correct:
print("Debug - Password matched!")

# store user id in session
session['user_id'] = str(user['id'])
session['username'] = username

# redirect to home page
return redirect(url_for('home'))
else:
# debugging
print("Debug - Password did NOT match")

# if the password is incorrect, return error message
return render_template("login.html", message="Incorrect password.")

# redirect to home page
return redirect(url_for('home'))
else:
# otherwise, if the user doesn't exist, return error message
print(f"Debug - Authentication failed for user: {username}")
return render_template("login.html", message="Username or password is incorrect.")

return render_template("login.html")
Expand All @@ -209,14 +182,14 @@ def reset_password():

# if the user exists, update the password
if user:
# hash the new password
hashed_password = generate_password_hash(new_password)

# update the user's password in the database
db.update_user_password(user['id'], hashed_password)
# Use reset_user_password function which handles hashing internally
success = db.reset_user_password(username, new_password)

# redirect to login page
return redirect(url_for('login'))
if success:
flash("Password reset successfully! Please log in with your new password.", "success")
return redirect(url_for('login'))
else:
return render_template("reset-password.html", message="Failed to reset password. Please try again.")

else:
# if the user doesn't exist, return error message
Expand All @@ -234,7 +207,7 @@ def prior_pieces():
if not username:
return redirect(url_for('login'))

# Get all stories for now (we'll filter by username later when that column is added)
# Get user-specific stories from database
stories = db.get_user_stories(username)

print(f"DEBUG MAIN - Retrieved {len(stories)} stories")
Expand Down Expand Up @@ -264,7 +237,7 @@ def my_account():
stories = db.get_user_stories(username)
story_count = len(stories)

# Handle None values for user stats
# Handle None values for user stats with proper fallback logic
total_words = user.get('total_word_count', 0) or 0
points = user.get('points', 0) or 0

Expand All @@ -283,7 +256,7 @@ def my_account():
@login_required
def save_writing():
if request.method == "POST":
# get info fromt the form
# get info from the form
written_raw = request.form.get('story')
title = request.form.get('title')

Expand All @@ -301,31 +274,31 @@ def save_writing():

print(f"DEBUG MAIN - Username: {username}")

# # Get current prompts
# if not story_prompts:
# story_prompts = prompts.gen_all_prompts()
# Get prompts from session instead of relying on global variable
story_prompts = session.get('current_prompts')
if not story_prompts:
print("DEBUG MAIN - No prompts in session, generating new ones")
story_prompts = prompts.gen_all_prompts() # Fallback if no prompts in session

print(f"DEBUG MAIN - Prompts: {story_prompts}")

# Calculate metrics
# Calculate metrics using the retrieved prompts
metrics = model.get_story_metrics(written_raw, story_prompts)
print(f"DEBUG MAIN - Metrics: {metrics}")

# Try to save the story with the minimal function first
# Save the story to database
story_id = db.add_story(title, written_raw, story_prompts, metrics['word_count'], metrics['points'], username)

if story_id is None:
print("DEBUG MAIN - Minimal save failed, trying full save")
# If minimal fails, try the full version
story_id = db.add_story(title, written_raw, story_prompts, metrics['word_count'], metrics['points'], username)

if story_id:
print(f"DEBUG MAIN - Story saved successfully with ID: {story_id}")


# Clear the used prompts from session after successful save
session.pop('current_prompts', None)

return render_template("congrats.html", title=title, story_len=metrics['word_count'], points=metrics['points'], words=metrics['num_used_prompts'])

else:
print("DEBUG MAIN - Both save methods failed")
print("DEBUG MAIN - Story save failed")
return render_template("index.html", message="Failed to save story. Check console for details.")

return redirect(url_for("home"))
Expand All @@ -340,7 +313,7 @@ def read_story(story_title):
if not username:
return redirect(url_for('login'))

# Get user's stories and find the specific one
# Get user's stories and find the specific one by title
stories = db.get_user_stories(username)
story = None

Expand All @@ -349,7 +322,7 @@ def read_story(story_title):
story = s
break

# If story not found, redirect to prior pieces
# If story not found, redirect to prior pieces with error message
if not story:
print(f"[ Error ] Story '{story_title}' not found.")
return redirect(url_for("prior_pieces"))
Expand All @@ -360,10 +333,53 @@ def read_story(story_title):
print(f"Error reading story: {e}")
return redirect(url_for("prior_pieces"))

# Test route for debugging database connection and operations
@app.route('/test-db')
@login_required
def test_db():
"""Test database connection and basic operations for debugging purposes"""
try:
# Test connection
connection_test = db.test_connection()

# Get current user
username = session.get('username')
user = db.get_user(username)

# Test story insertion with minimal data
test_prompts = {
"name": "Test Name",
"job": "Test Job",
"location": "Test Place",
"object": "Test Object",
"bonus": "Test Bonus"
}

story_id = db.add_story(
title="Test Story",
story_content="This is a test story to verify database functionality.",
prompts=test_prompts,
word_count=10,
points_earned=5,
username=username
)

return f"""
<h2>Database Test Results</h2>
<p>Connection Test: {'PASS' if connection_test else 'FAIL'}</p>
<p>User Found: {'PASS' if user else 'FAIL'}</p>
<p>Story Insert: {'PASS' if story_id else 'FAIL'}</p>
<p>Story ID: {story_id}</p>
<p>User Data: {user}</p>
"""

except Exception as e:
return f"<h2>Database Test Error</h2><p>{str(e)}</p>"

# logout route
@app.route('/logout')
def logout():
session.clear() # clear the session
session.clear() # clear the session data
return redirect('/login') # redirect to the login page

# mainloop
Expand Down
Loading