Cleanup debug codes

This commit is contained in:
2025-08-02 11:06:07 -04:00
parent a1ea3952a5
commit de007619ae
3 changed files with 5 additions and 1113 deletions
+5 -91
View File
@@ -145,7 +145,6 @@ class AttendanceData(db.Model):
'location_source': self.location_source 'location_source': self.location_source
} }
def generate_qr_url(name, qr_id): def generate_qr_url(name, qr_id):
"""Generate a unique URL for QR code destination""" """Generate a unique URL for QR code destination"""
# Clean the name for URL use # Clean the name for URL use
@@ -369,7 +368,7 @@ def users():
@app.route('/users/create', methods=['GET', 'POST']) @app.route('/users/create', methods=['GET', 'POST'])
@admin_required @admin_required
def create_user(): def create_user():
"""Create new user (Admin only) - Fixed validation""" """Create new user (Admin only)"""
if request.method == 'POST': if request.method == 'POST':
try: try:
full_name = request.form.get('full_name', '').strip() full_name = request.form.get('full_name', '').strip()
@@ -1150,7 +1149,7 @@ def qr_destination(qr_url):
@app.route('/qr/<string:qr_url>/checkin', methods=['POST']) @app.route('/qr/<string:qr_url>/checkin', methods=['POST'])
def qr_checkin(qr_url): def qr_checkin(qr_url):
"""FIXED: Enhanced staff check-in with guaranteed location saving""" """Enhanced staff check-in with guaranteed location saving"""
try: try:
# Find QR code by URL # Find QR code by URL
qr_code = QRCode.query.filter_by(qr_url=qr_url, active_status=True).first() qr_code = QRCode.query.filter_by(qr_url=qr_url, active_status=True).first()
@@ -1227,7 +1226,7 @@ def qr_checkin(qr_url):
if client_ip and ',' in client_ip: if client_ip and ',' in client_ip:
client_ip = client_ip.split(',')[0].strip() client_ip = client_ip.split(',')[0].strip()
# CRITICAL FIX: Enhanced location data processing with validation # Enhanced location data processing with validation
lat_value = None lat_value = None
lng_value = None lng_value = None
acc_value = None acc_value = None
@@ -1315,7 +1314,7 @@ def qr_checkin(qr_url):
has_coordinates = lat_value is not None and lng_value is not None has_coordinates = lat_value is not None and lng_value is not None
print(f" Has Valid Coordinates: {has_coordinates}") print(f" Has Valid Coordinates: {has_coordinates}")
# CRITICAL: Create attendance record with explicit location field assignment # Create attendance record with explicit location field assignment
print(f"\n💾 CREATING ATTENDANCE RECORD:") print(f"\n💾 CREATING ATTENDANCE RECORD:")
attendance = AttendanceData( attendance = AttendanceData(
@@ -1452,45 +1451,6 @@ def qr_checkin(qr_url):
'error': str(e) if app.debug else None 'error': str(e) if app.debug else None
}), 500 }), 500
# =====================================================================
# ADDITIONAL DEBUGGING ROUTE (Add this to your app.py for testing)
# =====================================================================
@app.route('/debug/attendance/<int:attendance_id>')
@admin_required
def debug_attendance(attendance_id):
"""Debug route to inspect a specific attendance record"""
try:
record = AttendanceData.query.get_or_404(attendance_id)
debug_info = {
'id': record.id,
'employee_id': record.employee_id,
'location_name': record.location_name,
'check_in_date': record.check_in_date.isoformat(),
'check_in_time': record.check_in_time.isoformat(),
'latitude': record.latitude,
'longitude': record.longitude,
'accuracy': record.accuracy,
'altitude': record.altitude,
'location_source': record.location_source,
'address': record.address,
'has_coordinates': record.latitude is not None and record.longitude is not None,
'created_timestamp': record.created_timestamp.isoformat() if record.created_timestamp else None
}
return jsonify({
'success': True,
'attendance_record': debug_info
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
def process_location_data(location_data): def process_location_data(location_data):
""" """
Process and validate location data from form Process and validate location data from form
@@ -1655,7 +1615,7 @@ def toggle_qr_status_api(qr_id):
return redirect(url_for('dashboard')) return redirect(url_for('dashboard'))
@app.route('/attendance') @app.route('/attendance')
#@admin_required @admin_required
def attendance_report(): def attendance_report():
"""Attendance report page (Admin only)""" """Attendance report page (Admin only)"""
try: try:
@@ -1708,52 +1668,6 @@ def attendance_report():
flash('Error loading attendance report.', 'error') flash('Error loading attendance report.', 'error')
return redirect(url_for('dashboard')) return redirect(url_for('dashboard'))
@app.route('/admin/location-stats')
@admin_required
def location_stats():
"""View location statistics and analytics"""
try:
# Basic stats
total_checkins = AttendanceData.query.count()
location_checkins = AttendanceData.query.filter(
AttendanceData.latitude.is_not(None),
AttendanceData.longitude.is_not(None)
).count()
# Recent check-ins with location data
recent_locations = AttendanceData.query.filter(
AttendanceData.latitude.is_not(None)
).order_by(AttendanceData.created_timestamp.desc()).limit(20).all()
# Accuracy statistics
accuracy_stats = {
'high': AttendanceData.query.filter(AttendanceData.accuracy <= 50).count(),
'medium': AttendanceData.query.filter(
AttendanceData.accuracy > 50,
AttendanceData.accuracy <= 100
).count(),
'low': AttendanceData.query.filter(AttendanceData.accuracy > 100).count(),
'unknown': AttendanceData.query.filter(AttendanceData.accuracy.is_(None)).count()
}
# Location source breakdown
source_stats = db.session.query(
AttendanceData.location_source,
db.func.count(AttendanceData.id).label('count')
).group_by(AttendanceData.location_source).all()
return render_template('location_stats.html',
total_checkins=total_checkins,
location_checkins=location_checkins,
recent_locations=recent_locations,
accuracy_stats=accuracy_stats,
source_stats=source_stats)
except Exception as e:
print(f"Error loading location stats: {e}")
flash('Error loading location statistics.', 'error')
return redirect(url_for('dashboard'))
@app.route('/api/attendance/stats') @app.route('/api/attendance/stats')
@admin_required @admin_required
def attendance_stats_api(): def attendance_stats_api():
-529
View File
@@ -1,529 +0,0 @@
#!/usr/bin/env python3
"""
Location Tracking Debug & Fix Script
====================================
This script will diagnose and fix location tracking issues in your QR system.
It will check the database, model, and provide the correct implementation.
Run this to identify why coordinates aren't being saved.
"""
import os
import sys
from datetime import datetime
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text, inspect
def create_app():
"""Create Flask app for debugging"""
app = Flask(__name__)
database_url = os.environ.get('DATABASE_URL', 'postgresql://postgres:postgres1411@localhost/qr_management')
app.config['SQLALCHEMY_DATABASE_URI'] = database_url
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
return app
def check_database_structure():
"""Check if location columns exist in database"""
print("🔍 STEP 1: Checking Database Structure")
print("=" * 50)
app = create_app()
db = SQLAlchemy(app)
with app.app_context():
try:
# Check if attendance_data table exists
result = db.session.execute(text("""
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_name = 'attendance_data'
);
"""))
if not result.fetchone()[0]:
print("❌ attendance_data table NOT found!")
return False
print("✅ attendance_data table exists")
# Check table structure
result = db.session.execute(text("""
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'attendance_data'
ORDER BY ordinal_position;
"""))
columns = result.fetchall()
print(f"\n📋 Table has {len(columns)} columns:")
location_columns = ['latitude', 'longitude', 'accuracy', 'altitude', 'location_source', 'address']
found_location_columns = []
for col_name, data_type, nullable, default in columns:
status = "🟢" if col_name in location_columns else ""
default_str = f" (default: {default})" if default else ""
print(f" {status} {col_name}: {data_type} {'NULL' if nullable == 'YES' else 'NOT NULL'}{default_str}")
if col_name in location_columns:
found_location_columns.append(col_name)
print(f"\n📊 Location columns found: {len(found_location_columns)}/6")
if len(found_location_columns) == 0:
print("❌ NO location columns found! Database migration needed.")
return False
elif len(found_location_columns) < 6:
missing = set(location_columns) - set(found_location_columns)
print(f"⚠️ Missing location columns: {', '.join(missing)}")
return False
else:
print("✅ All location columns present!")
return True
except Exception as e:
print(f"❌ Database check failed: {e}")
return False
def add_missing_columns():
"""Add missing location columns to database"""
print("\n🛠️ STEP 2: Adding Missing Location Columns")
print("=" * 50)
app = create_app()
db = SQLAlchemy(app)
with app.app_context():
try:
location_columns = [
"ALTER TABLE attendance_data ADD COLUMN IF NOT EXISTS latitude FLOAT",
"ALTER TABLE attendance_data ADD COLUMN IF NOT EXISTS longitude FLOAT",
"ALTER TABLE attendance_data ADD COLUMN IF NOT EXISTS accuracy FLOAT",
"ALTER TABLE attendance_data ADD COLUMN IF NOT EXISTS altitude FLOAT",
"ALTER TABLE attendance_data ADD COLUMN IF NOT EXISTS location_source VARCHAR(50) DEFAULT 'manual'",
"ALTER TABLE attendance_data ADD COLUMN IF NOT EXISTS address VARCHAR(500)"
]
for sql_command in location_columns:
try:
db.session.execute(text(sql_command))
column_name = sql_command.split()[4]
print(f"✅ Added: {column_name}")
except Exception as e:
print(f"⚠️ {sql_command.split()[4]}: {e}")
db.session.commit()
print("✅ Database columns added successfully!")
return True
except Exception as e:
print(f"❌ Failed to add columns: {e}")
db.session.rollback()
return False
def test_location_data_flow():
"""Test the complete location data flow"""
print("\n🧪 STEP 3: Testing Location Data Flow")
print("=" * 50)
app = create_app()
db = SQLAlchemy(app)
# Import your actual model
try:
sys.path.append(os.getcwd())
from app import AttendanceData
print("✅ Successfully imported AttendanceData model")
except Exception as e:
print(f"❌ Failed to import AttendanceData: {e}")
print(" Creating temporary model for testing...")
# Create temporary model
class AttendanceData(db.Model):
__tablename__ = 'attendance_data'
id = db.Column(db.Integer, primary_key=True)
qr_code_id = db.Column(db.Integer, nullable=False)
employee_id = db.Column(db.String(50), nullable=False)
check_in_date = db.Column(db.Date, nullable=False)
check_in_time = db.Column(db.Time, nullable=False)
latitude = db.Column(db.Float, nullable=True)
longitude = db.Column(db.Float, nullable=True)
accuracy = db.Column(db.Float, nullable=True)
altitude = db.Column(db.Float, nullable=True)
location_source = db.Column(db.String(50), default='manual')
address = db.Column(db.String(500), nullable=True)
location_name = db.Column(db.String(100), nullable=False)
status = db.Column(db.String(20), default='present')
created_timestamp = db.Column(db.DateTime, default=datetime.utcnow)
with app.app_context():
try:
# Test creating a record with location data
test_record = AttendanceData(
qr_code_id=1,
employee_id='TEST001',
check_in_date=datetime.today().date(),
check_in_time=datetime.now().time(),
latitude=37.7749,
longitude=-122.4194,
accuracy=15.0,
altitude=100.0,
location_source='gps',
address='San Francisco, CA',
location_name='Test Location',
status='present'
)
# Try to add without committing (just test)
db.session.add(test_record)
db.session.flush() # This will fail if columns don't exist
db.session.rollback() # Don't actually save the test record
print("✅ Location data model test passed!")
print(" Model can successfully store location coordinates")
return True
except Exception as e:
print(f"❌ Location data model test failed: {e}")
print(" Issue: Model doesn't have location fields or database columns missing")
db.session.rollback()
return False
def check_form_field_names():
"""Check the form field names in the frontend"""
print("\n📝 STEP 4: Checking Form Field Configuration")
print("=" * 50)
# Expected form field names based on your JavaScript
frontend_fields = [
'latitude',
'longitude',
'accuracy',
'altitude',
'location_source', # Note: JavaScript uses 'locationSource' but form submits as 'location_source'
'address'
]
# Expected server-side field names
server_fields = [
'latitude',
'longitude',
'accuracy',
'altitude',
'location_source',
'address'
]
print("📤 Frontend form fields:")
for field in frontend_fields:
print(f"{field}")
print("\n📥 Server expects these fields:")
for field in server_fields:
print(f"{field}")
print("\n⚠️ POTENTIAL ISSUE FOUND:")
print(" JavaScript uses 'locationSource' but form should submit 'location_source'")
print(" This might be causing the data not to save!")
return True
def generate_fixed_javascript():
"""Generate corrected JavaScript code"""
print("\n🔧 STEP 5: Generating Fixed JavaScript Code")
print("=" * 50)
fixed_js = '''
// FIXED: Update form fields with location data
function updateLocationFormFields() {
const fields = {
'latitude': userLocation.latitude || '',
'longitude': userLocation.longitude || '',
'accuracy': userLocation.accuracy || '',
'altitude': userLocation.altitude || '',
'location_source': userLocation.source || 'manual', // FIXED: was 'locationSource'
'address': userLocation.address || ''
};
Object.keys(fields).forEach(fieldId => {
const field = document.getElementById(fieldId);
if (field) {
field.value = fields[fieldId];
}
});
console.log('📝 Updated form fields with location data:', fields);
}
// FIXED: Submit function with correct field names
function submitCheckin(employeeId) {
if (isSubmitting) return false;
isSubmitting = true;
updateSubmitButton(true);
hideStatusMessage();
// Ensure location data is in the form
updateLocationFormFields();
// Prepare form data with CORRECT field names
const formData = new FormData();
formData.append('employee_id', employeeId);
// FIXED: Use correct field names that match server expectations
formData.append('latitude', userLocation.latitude || '');
formData.append('longitude', userLocation.longitude || '');
formData.append('accuracy', userLocation.accuracy || '');
formData.append('altitude', userLocation.altitude || '');
formData.append('location_source', userLocation.source || 'manual'); // FIXED
formData.append('address', userLocation.address || '');
// Debug: Log what we're sending
console.log('📤 Submitting form data:');
for (let [key, value] of formData.entries()) {
console.log(` ${key}: ${value}`);
}
const currentUrl = window.location.pathname;
const checkinUrl = `${currentUrl}/checkin`;
fetch(checkinUrl, {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
isSubmitting = false;
updateSubmitButton(false);
if (data.success) {
showSuccessPage(data);
console.log('✅ Check-in successful:', data);
stopLocationWatching();
} else {
showStatusMessage(data.message || 'Check-in failed', 'error');
console.log('❌ Check-in failed:', data.message);
}
})
.catch(error => {
isSubmitting = false;
updateSubmitButton(false);
showStatusMessage('Network error. Please check your connection and try again.', 'error');
console.error('❌ Network error:', error);
});
}
'''
print("✅ Fixed JavaScript code generated!")
print(" Key fixes:")
print(" - Changed 'locationSource' to 'location_source' in form fields")
print(" - Added debug logging to track form submission")
print(" - Ensured field names match server expectations")
return fixed_js
def generate_fixed_server_code():
"""Generate corrected server-side code"""
print("\n🔧 STEP 6: Generating Fixed Server Code")
print("=" * 50)
fixed_server = '''
@app.route('/qr/<string:qr_url>/checkin', methods=['POST'])
def qr_checkin(qr_url):
"""FIXED: Enhanced staff check-in with proper location handling"""
try:
# Find QR code
qr_code = QRCode.query.filter_by(qr_url=qr_url, active_status=True).first()
if not qr_code:
return jsonify({'success': False, 'message': 'QR code not found'}), 404
# Get form data
employee_id = request.form.get('employee_id', '').strip()
# FIXED: Get location data with correct field names
latitude = request.form.get('latitude', '').strip()
longitude = request.form.get('longitude', '').strip()
accuracy = request.form.get('accuracy', '').strip()
altitude = request.form.get('altitude', '').strip()
location_source = request.form.get('location_source', 'manual').strip() # FIXED
address = request.form.get('address', '').strip()
# DEBUG: Log received data
print(f"📥 Received location data:")
print(f" latitude: '{latitude}'")
print(f" longitude: '{longitude}'")
print(f" accuracy: '{accuracy}'")
print(f" altitude: '{altitude}'")
print(f" location_source: '{location_source}'")
print(f" address: '{address}'")
if not employee_id:
return jsonify({'success': False, 'message': 'Employee ID required'}), 400
# Validate employee ID
if not re.match(r'^[A-Za-z0-9]{3,20}$', employee_id):
return jsonify({'success': False, 'message': 'Invalid employee ID format'}), 400
# Check for duplicates
today = datetime.today()
existing = AttendanceData.query.filter_by(
qr_code_id=qr_code.id,
employee_id=employee_id.upper(),
check_in_date=today
).first()
if existing:
return jsonify({
'success': False,
'message': f'Already checked in at {existing.check_in_time.strftime("%H:%M")}'
}), 409
# FIXED: Process location data properly
lat_value = None
lng_value = None
acc_value = None
alt_value = None
try:
if latitude and latitude.strip() and latitude != 'null':
lat_value = float(latitude)
print(f"✅ Parsed latitude: {lat_value}")
if longitude and longitude.strip() and longitude != 'null':
lng_value = float(longitude)
print(f"✅ Parsed longitude: {lng_value}")
if accuracy and accuracy.strip() and accuracy != 'null':
acc_value = float(accuracy)
print(f"✅ Parsed accuracy: {acc_value}")
if altitude and altitude.strip() and altitude != 'null':
alt_value = float(altitude)
print(f"✅ Parsed altitude: {alt_value}")
except (ValueError, TypeError) as e:
print(f"⚠️ Location parsing error: {e}")
# Create attendance record
attendance = AttendanceData(
qr_code_id=qr_code.id,
employee_id=employee_id.upper(),
check_in_date=today,
check_in_time=datetime.now().time(),
location_name=qr_code.location,
status='present'
)
# FIXED: Add location data to model
if lat_value is not None:
attendance.latitude = lat_value
if lng_value is not None:
attendance.longitude = lng_value
if acc_value is not None:
attendance.accuracy = acc_value
if alt_value is not None:
attendance.altitude = alt_value
if location_source:
attendance.location_source = location_source
if address:
attendance.address = address
print(f"💾 Saving attendance with location: lat={attendance.latitude}, lng={attendance.longitude}")
db.session.add(attendance)
db.session.commit()
# Verify data was saved
saved_record = AttendanceData.query.get(attendance.id)
print(f"✅ Saved record: lat={saved_record.latitude}, lng={saved_record.longitude}")
return jsonify({
'success': True,
'message': 'Check-in successful!',
'data': {
'employee_id': employee_id.upper(),
'location': qr_code.location,
'has_location': saved_record.latitude is not None and saved_record.longitude is not None,
'coordinates': f"{saved_record.latitude}, {saved_record.longitude}" if saved_record.latitude else "No GPS data"
}
})
except Exception as e:
print(f"❌ Check-in error: {e}")
db.session.rollback()
return jsonify({'success': False, 'message': str(e)}), 500
'''
print("✅ Fixed server code generated!")
print(" Key fixes:")
print(" - Added debug logging for received form data")
print(" - Improved location data parsing and validation")
print(" - Added verification that data was actually saved")
print(" - Better error handling and feedback")
return fixed_server
def main():
"""Main diagnostic function"""
print("🩺 QR LOCATION TRACKING DIAGNOSTIC TOOL")
print("=" * 60)
print("This tool will identify why coordinates aren't being saved.")
print("=" * 60)
issues_found = []
# Step 1: Check database structure
if not check_database_structure():
issues_found.append("Database missing location columns")
print("\n🛠️ FIXING: Adding missing database columns...")
if add_missing_columns():
print("✅ Database columns fixed!")
else:
print("❌ Failed to fix database - manual intervention needed")
return
# Step 2: Test model
if not test_location_data_flow():
issues_found.append("Model can't handle location data")
# Step 3: Check form fields
check_form_field_names()
issues_found.append("Form field name mismatch")
# Step 4: Generate fixes
print("\n" + "=" * 60)
print("🎯 DIAGNOSIS COMPLETE")
print("=" * 60)
if issues_found:
print(f"❌ Found {len(issues_found)} issues:")
for i, issue in enumerate(issues_found, 1):
print(f" {i}. {issue}")
print(f"\n🔧 SOLUTIONS:")
print(f"1. Run the database migration script if not done already")
print(f"2. Update your JavaScript with the fixed code above")
print(f"3. Update your server route with the fixed code above")
print(f"4. Add debug logging to track the data flow")
else:
print("✅ No major issues found!")
print(" Location tracking should be working.")
print(" If still having issues, check browser console for errors.")
# Generate fixed files
print(f"\n📁 FIXED CODE FILES:")
print(f"1. Save the fixed JavaScript to your qr_destination.js")
print(f"2. Update your app.py qr_checkin route")
print(f"3. Test with a mobile device to verify GPS functionality")
generate_fixed_javascript()
generate_fixed_server_code()
if __name__ == '__main__':
main()
-493
View File
@@ -1,493 +0,0 @@
#!/usr/bin/env python3
"""
User Management Test Script
Test all user management functionalities to ensure they work correctly
"""
import requests
import json
from datetime import datetime
class UserManagementTester:
def __init__(self, base_url="http://localhost:5000"):
self.base_url = base_url
self.session = requests.Session()
self.admin_session_id = None
def login_as_admin(self, username="admin", password="admin123"):
"""Login as admin to test admin functions"""
print("🔐 Testing admin login...")
response = self.session.post(f"{self.base_url}/login", data={
'username': username,
'password': password
})
if response.status_code == 200 and "Welcome" in response.text:
print("✅ Admin login successful")
return True
else:
print("❌ Admin login failed")
return False
def test_create_user(self):
"""Test user creation functionality"""
print("\n👤 Testing user creation...")
test_user_data = {
'full_name': 'Test User',
'email': 'testuser@example.com',
'username': 'testuser',
'password': 'testpass123',
'role': 'staff'
}
response = self.session.post(f"{self.base_url}/users/create", data=test_user_data)
if response.status_code == 200:
if "created successfully" in response.text or response.url.endswith('/users'):
print("✅ User creation successful")
return True
print("❌ User creation failed")
print(f"Response status: {response.status_code}")
return False
def test_users_page_access(self):
"""Test access to users management page"""
print("\n📋 Testing users page access...")
response = self.session.get(f"{self.base_url}/users")
if response.status_code == 200 and "User Management" in response.text:
print("✅ Users page accessible")
return True
else:
print("❌ Users page access failed")
print(f"Response status: {response.status_code}")
return False
def get_test_user_id(self):
"""Get the ID of the test user for further testing"""
print("\n🔍 Finding test user ID...")
response = self.session.get(f"{self.base_url}/users")
if response.status_code == 200:
# Parse HTML to find user ID - this is a simple approach
# In a real test, you'd use BeautifulSoup or similar
content = response.text
if "testuser" in content:
print("✅ Test user found in users list")
# For demo purposes, we'll assume user ID 2 (after admin)
return 2
print("❌ Could not find test user")
return None
def test_user_promotion(self, user_id):
"""Test promoting a user to admin"""
print(f"\n⬆️ Testing user promotion (ID: {user_id})...")
response = self.session.get(f"{self.base_url}/users/{user_id}/promote")
if response.status_code == 200 or response.status_code == 302:
print("✅ User promotion request successful")
return True
else:
print("❌ User promotion failed")
print(f"Response status: {response.status_code}")
return False
def test_user_demotion(self, user_id):
"""Test demoting a user from admin to staff"""
print(f"\n⬇️ Testing user demotion (ID: {user_id})...")
response = self.session.get(f"{self.base_url}/users/{user_id}/demote")
if response.status_code == 200 or response.status_code == 302:
print("✅ User demotion request successful")
return True
else:
print("❌ User demotion failed")
print(f"Response status: {response.status_code}")
return False
def test_user_deactivation(self, user_id):
"""Test deactivating a user"""
print(f"\n🚫 Testing user deactivation (ID: {user_id})...")
response = self.session.get(f"{self.base_url}/users/{user_id}/delete")
if response.status_code == 200 or response.status_code == 302:
print("✅ User deactivation request successful")
return True
else:
print("❌ User deactivation failed")
print(f"Response status: {response.status_code}")
return False
def test_user_reactivation(self, user_id):
"""Test reactivating a user"""
print(f"\n✅ Testing user reactivation (ID: {user_id})...")
response = self.session.get(f"{self.base_url}/users/{user_id}/reactivate")
if response.status_code == 200 or response.status_code == 302:
print("✅ User reactivation request successful")
return True
else:
print("❌ User reactivation failed")
print(f"Response status: {response.status_code}")
return False
def test_user_edit(self, user_id):
"""Test editing user information"""
print(f"\n✏️ Testing user edit (ID: {user_id})...")
# First get the edit page
response = self.session.get(f"{self.base_url}/users/{user_id}/edit")
if response.status_code == 200:
print("✅ User edit page accessible")
# Test submitting updated user data
updated_data = {
'full_name': 'Updated Test User',
'email': 'updated_testuser@example.com',
'role': 'staff'
}
response = self.session.post(f"{self.base_url}/users/{user_id}/edit", data=updated_data)
if response.status_code == 200 or response.status_code == 302:
print("✅ User edit submission successful")
return True
print("❌ User edit failed")
print(f"Response status: {response.status_code}")
return False
def test_bulk_operations(self):
"""Test bulk user operations"""
print("\n📦 Testing bulk operations...")
# Test bulk deactivate API
test_data = {'user_ids': [2]} # Assuming test user has ID 2
response = self.session.post(
f"{self.base_url}/users/bulk/deactivate",
json=test_data,
headers={'Content-Type': 'application/json'}
)
if response.status_code == 200:
try:
result = response.json()
if result.get('success'):
print("✅ Bulk deactivate API works")
# Test bulk activate
response = self.session.post(
f"{self.base_url}/users/bulk/activate",
json=test_data,
headers={'Content-Type': 'application/json'}
)
if response.status_code == 200:
result = response.json()
if result.get('success'):
print("✅ Bulk activate API works")
return True
except json.JSONDecodeError:
pass
print("❌ Bulk operations failed")
return False
def test_admin_protections(self):
"""Test admin protection mechanisms"""
print("\n🛡️ Testing admin protection mechanisms...")
# Try to deactivate admin user (should fail)
response = self.session.get(f"{self.base_url}/users/1/delete") # Assuming admin is ID 1
# This should either redirect or show an error
if response.status_code in [200, 302]:
print("✅ Admin self-deactivation protection works")
return True
else:
print("❌ Admin protection test inconclusive")
return False
def run_all_tests(self):
"""Run comprehensive user management tests"""
print("🧪 Starting User Management Tests")
print("=" * 50)
results = []
# Test 1: Admin Login
results.append(("Admin Login", self.login_as_admin()))
if not results[-1][1]:
print("\n❌ Cannot proceed without admin login")
return False
# Test 2: Users Page Access
results.append(("Users Page Access", self.test_users_page_access()))
# Test 3: User Creation
results.append(("User Creation", self.test_create_user()))
# Get test user ID for further tests
test_user_id = self.get_test_user_id()
if test_user_id:
# Test 4: User Edit
results.append(("User Edit", self.test_user_edit(test_user_id)))
# Test 5: User Promotion
results.append(("User Promotion", self.test_user_promotion(test_user_id)))
# Test 6: User Demotion
results.append(("User Demotion", self.test_user_demotion(test_user_id)))
# Test 7: User Deactivation
results.append(("User Deactivation", self.test_user_deactivation(test_user_id)))
# Test 8: User Reactivation
results.append(("User Reactivation", self.test_user_reactivation(test_user_id)))
# Test 9: Bulk Operations
results.append(("Bulk Operations", self.test_bulk_operations()))
# Test 10: Admin Protections
results.append(("Admin Protections", self.test_admin_protections()))
# Print Results Summary
print("\n" + "=" * 50)
print("📊 TEST RESULTS SUMMARY")
print("=" * 50)
passed = 0
total = len(results)
for test_name, result in results:
status = "✅ PASS" if result else "❌ FAIL"
print(f"{test_name:<25} {status}")
if result:
passed += 1
print("-" * 50)
print(f"Total Tests: {total}")
print(f"Passed: {passed}")
print(f"Failed: {total - passed}")
print(f"Success Rate: {(passed/total)*100:.1f}%")
if passed == total:
print("\n🎉 ALL TESTS PASSED! User management is working correctly.")
elif passed >= total * 0.8:
print("\n⚠️ Most tests passed, but some issues need attention.")
else:
print("\n❌ Multiple tests failed. User management needs debugging.")
return passed == total
def manual_test_instructions():
"""Print manual testing instructions"""
print("\n" + "=" * 60)
print("📋 MANUAL TESTING INSTRUCTIONS")
print("=" * 60)
print("""
To manually test user management functionalities:
1. 🔐 Login as Admin:
- Go to /login
- Use: admin / admin123
- Verify you can access admin features
2. 👥 Access User Management:
- Go to /users
- Verify you see the user management page
- Check that statistics are displayed correctly
3. Create New User:
- Click "Add New User"
- Fill in: Name, Email, Username, Password, Role
- Submit and verify user appears in list
4. Edit User:
- Click dropdown next to any user (not yourself)
- Select "Edit User"
- Change name/email/role and save
- Verify changes appear in users list
5. Promote User:
- Find a staff user in the list
- Click dropdown "Promote to Admin"
- Confirm the action
- Verify user role changes to Admin
6. Demote User:
- Find an admin user (not yourself)
- Click dropdown "Demote to Staff"
- Confirm the action
- Verify user role changes to Staff
7. 🚫 Deactivate User:
- Find any user (not yourself)
- Click dropdown "Deactivate User"
- Confirm the action
- Verify user status changes to Inactive
8. Reactivate User:
- Find an inactive user
- Click dropdown "Reactivate User"
- Confirm the action
- Verify user status changes to Active
9. 📦 Bulk Operations:
- Click "Bulk Actions" button
- Select multiple users with checkboxes
- Try "Activate Selected" or "Deactivate Selected"
- Verify changes are applied to all selected users
10. 🛡 Admin Protection Tests:
- Try to deactivate yourself (should fail with error)
- Try to demote the last admin (should fail with error)
- Verify these protections work as expected
🔍 What to Look For:
- Flash messages appear for success/error states
- Page redirects work correctly after actions
- User list updates to reflect changes
- Protection mechanisms prevent dangerous actions
- Statistics update correctly after changes
- Search and filtering work properly
""")
def quick_setup_guide():
"""Print setup guide for user management"""
print("\n" + "=" * 60)
print("🚀 QUICK SETUP GUIDE")
print("=" * 60)
print("""
If user management functions are not working, check:
1. 📂 File Updates:
- Replace user management routes in app.py
- Update users.html template
- Ensure admin_required decorator is properly implemented
2. 🗄 Database Check:
- Verify users table exists
- Check that admin user exists with correct role
- Ensure foreign key relationships are set up
3. 🔧 Code Integration:
- Add the enhanced routes to your app.py
- Import required modules (datetime, etc.)
- Ensure session management is working
4. 🎨 Template Updates:
- Replace templates/users.html with enhanced version
- Verify CSS variables are defined in style.css
- Check JavaScript functions are loading
5. Configuration:
- Ensure Flask app has proper secret key
- Database connection is working
- Session configuration is correct
6. 🧪 Testing:
- Start with manual testing first
- Check browser console for JavaScript errors
- Verify network requests in browser dev tools
- Check Flask console for Python errors
Common Issues & Solutions:
- 404 errors: Routes not properly registered
- 500 errors: Database connection or Python syntax issues
- Permission denied: admin_required decorator not working
- JavaScript errors: Check for missing functions in templates
- CSS issues: Verify CSS variables are defined
""")
def run_database_check():
"""Check if database is properly set up for user management"""
print("\n🗄️ DATABASE SETUP CHECK")
print("=" * 40)
try:
import sqlite3
import os
# This is a basic check - adjust based on your database setup
print("✅ Database modules available")
# You could add actual database connectivity tests here
print("📝 To verify your database:")
print(" 1. Check that users table exists")
print(" 2. Verify admin user exists")
print(" 3. Test database connectivity")
print(" 4. Check foreign key constraints")
return True
except Exception as e:
print(f"❌ Database check failed: {e}")
return False
if __name__ == "__main__":
print("🧪 QR Code Management - User Management Tester")
print("=" * 60)
# Check if Flask app is running
tester = UserManagementTester()
try:
# Quick connectivity test
response = tester.session.get(f"{tester.base_url}/")
if response.status_code in [200, 302, 404]: # Any response means server is running
print("✅ Flask application appears to be running")
# Ask user what they want to do
print("\nSelect testing mode:")
print("1. 🤖 Run automated tests")
print("2. 📋 Show manual testing instructions")
print("3. 🚀 Show setup guide")
print("4. 🗄️ Check database setup")
choice = input("\nEnter choice (1-4): ").strip()
if choice == "1":
print("\n🤖 Running automated tests...")
success = tester.run_all_tests()
if not success:
print("\n💡 If tests failed, try the manual testing instructions.")
elif choice == "2":
manual_test_instructions()
elif choice == "3":
quick_setup_guide()
elif choice == "4":
run_database_check()
else:
print("Invalid choice. Showing manual instructions...")
manual_test_instructions()
else:
print("❌ Cannot connect to Flask application")
print("Make sure your app is running on http://localhost:5000")
except Exception as e:
print(f"❌ Connection failed: {e}")
print("\n🔧 Troubleshooting:")
print("1. Make sure Flask app is running: python app.py")
print("2. Check the URL is correct: http://localhost:5000")
print("3. Verify no firewall is blocking the connection")
print("\n📋 Showing manual testing instructions instead...")
manual_test_instructions()