From de007619aed33b214fed34f2aa10a0c87ab12990 Mon Sep 17 00:00:00 2001 From: Nguyen Ngo Date: Sat, 2 Aug 2025 11:06:07 -0400 Subject: [PATCH] Cleanup debug codes --- app.py | 96 +------ location_debug_script.py | 529 --------------------------------------- user_management_test.py | 493 ------------------------------------ 3 files changed, 5 insertions(+), 1113 deletions(-) delete mode 100644 location_debug_script.py delete mode 100644 user_management_test.py diff --git a/app.py b/app.py index 02d8cad..78f76cc 100644 --- a/app.py +++ b/app.py @@ -145,7 +145,6 @@ class AttendanceData(db.Model): 'location_source': self.location_source } - def generate_qr_url(name, qr_id): """Generate a unique URL for QR code destination""" # Clean the name for URL use @@ -369,7 +368,7 @@ def users(): @app.route('/users/create', methods=['GET', 'POST']) @admin_required def create_user(): - """Create new user (Admin only) - Fixed validation""" + """Create new user (Admin only)""" if request.method == 'POST': try: full_name = request.form.get('full_name', '').strip() @@ -1150,7 +1149,7 @@ def qr_destination(qr_url): @app.route('/qr//checkin', methods=['POST']) def qr_checkin(qr_url): - """FIXED: Enhanced staff check-in with guaranteed location saving""" + """Enhanced staff check-in with guaranteed location saving""" try: # Find QR code by URL 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: client_ip = client_ip.split(',')[0].strip() - # CRITICAL FIX: Enhanced location data processing with validation + # Enhanced location data processing with validation lat_value = None lng_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 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:") attendance = AttendanceData( @@ -1452,45 +1451,6 @@ def qr_checkin(qr_url): 'error': str(e) if app.debug else None }), 500 -# ===================================================================== -# ADDITIONAL DEBUGGING ROUTE (Add this to your app.py for testing) -# ===================================================================== - -@app.route('/debug/attendance/') -@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): """ Process and validate location data from form @@ -1655,7 +1615,7 @@ def toggle_qr_status_api(qr_id): return redirect(url_for('dashboard')) @app.route('/attendance') -#@admin_required +@admin_required def attendance_report(): """Attendance report page (Admin only)""" try: @@ -1708,52 +1668,6 @@ def attendance_report(): flash('Error loading attendance report.', 'error') 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') @admin_required def attendance_stats_api(): diff --git a/location_debug_script.py b/location_debug_script.py deleted file mode 100644 index 3eae5f9..0000000 --- a/location_debug_script.py +++ /dev/null @@ -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//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() \ No newline at end of file diff --git a/user_management_test.py b/user_management_test.py deleted file mode 100644 index 8c0a7c9..0000000 --- a/user_management_test.py +++ /dev/null @@ -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() \ No newline at end of file