Skip to content
Draft
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: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ __pycache__/
# C extensions
*.so

# Downloaded files
downloaded_files/

# Distribution / packaging
.Python
build/
Expand Down
90 changes: 90 additions & 0 deletions templates/admin.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<!DOCTYPE html>
<html>
<head>
<title>Admin - Activity Log</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
body {
font-family: Arial, sans-serif;
margin: 2rem;
}
h1 {
text-align: center;
}
.table-container {
margin-top: 2rem;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 10px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #007bff;
color: white;
}
tr:hover {
background-color: #f5f5f5;
}
</style>
</head>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="nav-container" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="{{ url_for('about') }}">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('profile') }}">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('admin') }}">Admin</a>
</li>
<li class="nav-item" style="float: right;">
<a class="nav-link" href="{{ url_for('logout') }}">Logout</a>
</li>
</ul>
</div>
</nav>
<body>
<br>
<h1>Activity Log</h1>
<br>
<div class="table-container">
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Timestamp</th>
<th>Activity Type</th>
<th>Username</th>
<th>Details</th>
</tr>
</thead>
<tbody>
{% for activity in activities %}
<tr>
<td>{{ activity[0] }}</td>
<td>{{ activity[1] }}</td>
<td>{{ activity[2] }}</td>
<td>{{ activity[3] }}</td>
<td>{{ activity[4] }}</td>
</tr>
{% endfor %}
{% if not activities %}
<tr>
<td colspan="5" style="text-align: center;">No activity logged yet.</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</body>
</html>
3 changes: 3 additions & 0 deletions templates/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
<li class="nav-item">
<a class="nav-link" href="{{ url_for('profile') }}">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('admin') }}">Admin</a>
</li>
<li class="nav-item" style="float: right;">
<a class="nav-link" href="{{ url_for('logout') }}">Logout</a>
</li>
Expand Down
3 changes: 3 additions & 0 deletions templates/wine.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@
<li class="nav-item">
<a class="nav-link" href="{{ url_for('profile') }}">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('admin') }}">Admin</a>
</li>
<li class="nav-item" style="float: right;">
<a class="nav-link" href="{{ url_for('logout') }}">Logout</a>
</li>
Expand Down
50 changes: 50 additions & 0 deletions tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from seleniumbase import BaseCase

class AdminTests(BaseCase):
def login(self):
# open login page
self.open("http://127.0.0.1:5000/login")
self.type("#username", "jefferyepayne")
self.type("#password", "TestTest")
self.click('button[type="submit"]')
self.wait_for_text("Logged in successfully!")
self.assert_text("Logged in successfully!")

def logout(self):
self.click_link("Logout")
self.assert_element("#username")

def test_admin_page_accessible(self):
"""Test that the admin page is accessible after login"""
self.login()

# Navigate to admin page
self.click_link("Admin")

# Verify admin page elements
self.assert_text("Activity Log")
self.assert_element("table")

self.logout()

def test_admin_page_shows_login_activity(self):
"""Test that login activity is logged and shown on admin page"""
self.login()

# Navigate to admin page
self.click_link("Admin")

# Verify login activity is shown
self.assert_text("login")
self.assert_text("Successful login")

self.logout()

def test_admin_page_requires_login(self):
"""Test that admin page redirects to login if not logged in"""
# Try to access admin page directly without login
self.open("http://127.0.0.1:5000/admin")

# Should be redirected to login page
self.assert_element("#username")
self.assert_element("#password")
52 changes: 52 additions & 0 deletions wine.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import pickle
import signal
import re
from datetime import datetime

# Create a signal handler to shut down the server gracefully on shutdown
def shutdown(signal_number, frame):
Expand All @@ -39,6 +40,37 @@ def shutdown(signal_number, frame):
# Turn debugging mode off for production
app.debug = True

# Initialize activity_log table if it doesn't exist
def init_activity_log():
conn = sqlite3.connect('wineusers.db')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS activity_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL,
activity_type TEXT NOT NULL,
username TEXT,
details TEXT
)
''')
conn.commit()
conn.close()

# Log an activity to the database
def log_activity(activity_type, username=None, details=None):
conn = sqlite3.connect('wineusers.db')
cursor = conn.cursor()
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
cursor.execute(
'INSERT INTO activity_log (timestamp, activity_type, username, details) VALUES (?, ?, ?, ?)',
(timestamp, activity_type, username, details)
)
conn.commit()
conn.close()

# Initialize the activity log table on startup
init_activity_log()

# create a routine to notify test scipts that the server is up and running
@app.route("/health")
def health_check():
Expand Down Expand Up @@ -68,9 +100,11 @@ def login():
session['id'] = account[0]
session['username'] = account[1]
session['email'] = account[3]
log_activity('login', username, 'Successful login')
msg = 'Logged in successfully!'
return render_template('wine.html', prediction_text = msg)
else:
log_activity('login', username, 'Failed login attempt')
msg = 'Incorrect username/password!'
else:
msg = 'Database connection error!'
Expand Down Expand Up @@ -122,6 +156,7 @@ def register():
cursor.close()
# cursor.execute('INSERT INTO accounts VALUES (NULL, % s, % s, % s)', (username, password, email, ))
# mysql.connection.commit()
log_activity('registration', username, 'New user registered with email: ' + email)
msg = 'You have successfully registered!'
elif request.method == 'POST':
msg = 'Please fill out the form!'
Expand Down Expand Up @@ -163,6 +198,9 @@ def predict():
prediction = model.predict([[alcohol, malic_acid, ash, alcalinity_of_ash, magnesium, total_phenols, flavanoids, nonflavanoid_phenols, proanthocyanins, color_intensity, hue, od280_od315_of_diluted_wines, proline]])
prediction = prediction[0]

username = session.get('username', 'anonymous')
log_activity('prediction', username, 'Predicted wine variety #{}'.format(prediction))

return render_template('wine.html', prediction_text='The wine type is variety #{}'.format(prediction))

# change_password allows users to change their password if they are logged in
Expand Down Expand Up @@ -196,6 +234,20 @@ def change_password():
else:
return redirect(url_for('login'))

# admin displays the activity log for administrators
@app.route("/admin")
def admin():
if 'username' in session:
conn = sqlite3.connect('wineusers.db')
cursor = conn.cursor()
cursor.execute('SELECT * FROM activity_log ORDER BY timestamp DESC')
activities = cursor.fetchall()
cursor.close()
conn.close()
return render_template('admin.html', activities=activities)
else:
return redirect(url_for('login'))

# main runs the Flask web application
if __name__ == "__main__":
# app.debug = True
Expand Down
Binary file modified wineusers.db
Binary file not shown.