Cleanup debug codes
This commit is contained in:
@@ -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():
|
||||||
|
|||||||
@@ -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()
|
|
||||||
@@ -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()
|
|
||||||
Reference in New Issue
Block a user