Skip to content

Commit

Permalink
Merge pull request #22 from Strong-AI-Lab/JVNAUTOSCI-111-Implement-a-…
Browse files Browse the repository at this point in the history
…web-front-end-for-Von-suitable-for-local-or-server-use

JVNAUTOSCI-111-Implement-a-web-front-end-for-Von-suitable-for-local-or-server-use
  • Loading branch information
sjin824 authored Oct 6, 2024
2 parents d455bf6 + 3dcf9e4 commit af48b97
Show file tree
Hide file tree
Showing 20 changed files with 493 additions and 20 deletions.
13 changes: 0 additions & 13 deletions paper_recommender/requirements.txt

This file was deleted.

6 changes: 6 additions & 0 deletions src/tell_von/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ openai>=1.28.1
# Flask framework for creating web applications in Python
Flask>=3.0.3

# Testing frameworks
pytest>=7.4.0
pytest-flask>=1.2.0

# # Package 1
# package1==1.0.0

Expand Down Expand Up @@ -97,6 +101,7 @@ debugpy==1.8.1
distro==1.9.0
exceptiongroup==1.2.1
Flask>=3.0.3
flask_testing>=0.8.1
gcloud==0.18.3
google-api-core==2.19.0
google-api-python-client==2.128.0
Expand Down Expand Up @@ -129,6 +134,7 @@ pyasn1==0.6.0
pyasn1_modules==0.4.0
pydantic==2.7.1
pydantic_core==2.18.2
pymongo>=4.10.1 # AND MAKE SURE SEPARATE bson IS NOT INSTALLED
pyparsing==3.1.2
pysimplegui>=5.0.5
requests==2.31.0
Expand Down
5 changes: 2 additions & 3 deletions src/vonlib/database_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ def get_local_client():
client = MongoClient('mongodb://localhost:27017/')
return client
except errors.ConnectionError as e:
print(f"Error connecting to MongoDB: {e}")
return None
raise RuntimeError(f"Error connecting to MongoDB: {e}")

class DatabaseDriver:

Expand Down Expand Up @@ -198,7 +197,7 @@ def create_database(self, database_name):
dblist = self.client.list_database_names()

if database_name not in dblist:
db = self.client[database_name]
db = self.client[database_name] # This will create a new database only if data is stored in it.
return db
else:
return None
Expand Down
Empty file added src/web_interface/__init__.py
Empty file.
Binary file not shown.
Binary file not shown.
141 changes: 141 additions & 0 deletions src/web_interface/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
from flask import Flask, render_template, redirect, url_for, request, flash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user

from werkzeug.security import generate_password_hash, check_password_hash
from bson.objectid import ObjectId
import re

from pymongo import MongoClient
from flask_pymongo import PyMongo #needed?
import argparse
import os
from web_interface.user_data import VonUser # work our why it doesn't like this import when it's in the same directory


#For details, see https://naoinstitute.atlassian.net/browse/JVNAUTOSCI-111
# write a minimal local web page server, that includes login and identity tracking, and
# is easy to redeploy on a remote server (e.g. one running on aws or google cloud or Azure).
# I want to be able to easily call python backend code from the server.
# Make sure the start code for the local server has an option to run in foreground or background.
app = Flask(__name__)
app.secret_key = os.getenv("VON_APP_SECRET_KEY") # Replace with a secure secret key in production

# Configure Flask-Login
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

# User loader callback
@login_manager.user_loader
def load_user(user_id):
user = VonUser.find_by_id(user_id)
if user:
return User(user.get_username())
return None

# User class
class User(UserMixin):
def __init__(self, username):
self.vonuser = VonUser(username)
self.username = username
self.id = self.vonuser.get_id()
#str(mongo.db.users.find_one({"username": username})['_id'])

# Routes
@app.route('/')
def home():
return render_template('home.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
hpass=generate_password_hash(password, method='pbkdf2:sha256')

# Verify username and password
user = VonUser.find_by_username(username)
if user and check_password_hash(hpass,password): # In production, use hashed passwords
login_user(User(username))
return redirect(url_for('dashboard'))
else:
flash('Login Unsuccessful. Please try again or signup.', 'error')
return redirect(url_for('login'))
#return 'Invalid credentials', 401

return render_template('login.html')

@app.route('/signup', methods=['GET', 'POST'])
def signup():
if request.method == 'POST':
# Get form data
username = request.form['username']
email = request.form['email']
password = request.form['password']
confirm_password = request.form['confirm_password']

# Validate form data
if not username or not email or not password or not confirm_password:
flash('Please fill out all fields.', 'error')
return redirect(url_for('signup'))

if password != confirm_password:
flash('Passwords do not match.', 'error')
return redirect(url_for('signup'))

existing_user = VonUser.find_by_username(username)
# Check if user already exists
if existing_user and username == existing_user.get_username():
flash('Username already exists.', 'error')
return redirect(url_for('signup'))

#Validate email format
email_regex = r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
if not re.match(email_regex, email):
flash('Invalid email format.', 'error')
return redirect(url_for('signup'))

hpass=generate_password_hash(password, method='pbkdf2:sha256')
newUser=VonUser.create_user(username, email, hpass) #create_user is a class method
if not newUser:
flash('Error creating user.', 'error')
return redirect(url_for('signup'))


flash('Registration successful. Please log in.', 'success')
return redirect(url_for('login'))

return render_template('signup.html')


@app.route('/dashboard')
@login_required
def dashboard():
return render_template('dashboard.html', user=current_user.username)

@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('home'))

# Main function
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Run the Flask web server.')
parser.add_argument('--host', default='127.0.0.1', help='Host to listen on.')
parser.add_argument('--port', default=5000, type=int, help='Port to listen on.')
parser.add_argument('--background', action='store_true', help='Run server in the background.')
args = parser.parse_args()

if args.background:
from multiprocessing import Process


def run_app():
app.run(host=args.host, port=args.port)

p = Process(target=run_app)
p.start()
print(f"Server running in background on {args.host}:{args.port} (PID: {p.pid})")
else:
app.run(host=args.host, port=args.port)
12 changes: 12 additions & 0 deletions src/web_interface/templates/dashboard.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dashboard</title>
</head>
<body>
<h1>Dashboard</h1>
<p>Welcome, {{ user }}!</p>
<p><a href="{{ url_for('logout') }}">Logout</a></p>
</body>
</html>
17 changes: 17 additions & 0 deletions src/web_interface/templates/home.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Von</title>
</head>
<body>
<h1>Welcome to Von</h1>
{% if current_user.is_authenticated %}
<p>Hello, {{ current_user.username }}!</p>
<p><a href="{{ url_for('dashboard') }}">Go to Dashboard</a></p>
<p><a href="{{ url_for('logout') }}">Logout</a></p>
{% else %}
<p><a href="{{ url_for('login') }}">Login</a></p>
{% endif %}
</body>
</html>
31 changes: 31 additions & 0 deletions src/web_interface/templates/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<!-- Display flash messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<ul>
{% for category, message in messages %}
<li style="color: {% if category == 'error' %}red{% else %}green{% endif %};">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<!-- Login form -->
<form action="{{ url_for('login') }}" method="post">
<label for="username">Username:</label><br>
<input type="text" name="username" id="username" required autofocus><br><br>

<label for="password">Password:</label><br>
<input type="password" name="password" id="password" required><br><br>

<button type="submit">Login</button>
</form>
<p>Don't have an account? <a href="{{ url_for('signup') }}">Sign up here</a>.</p>
</body>
</html>
37 changes: 37 additions & 0 deletions src/web_interface/templates/signup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Signup</title>
</head>
<body>
<h1>Sign Up</h1>
<!-- Display flash messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<ul>
{% for category, message in messages %}
<li style="color: {% if category == 'error' %}red{% else %}green{% endif %};">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<!-- Signup form -->
<form action="{{ url_for('signup') }}" method="post">
<label for="username">Username:</label><br>
<input type="text" name="username" id="username" required autofocus><br><br>

<label for="email">Email:</label><br>
<input type="text" name="email" id="email" required><br><br>

<label for="password">Password:</label><br>
<input type="password" name="password" id="password" required><br><br>

<label for="confirm_password">Confirm Password:</label><br>
<input type="password" name="confirm_password" id="confirm_password" required><br><br>

<button type="submit">Register</button>
</form>
<p>Already have an account? <a href="{{ url_for('login') }}">Log in here</a>.</p>
</body>
</html>
Loading

0 comments on commit af48b97

Please sign in to comment.