Cleanup debug codes
This commit is contained in:
@@ -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/<string:qr_url>/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/<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):
|
||||
"""
|
||||
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():
|
||||
|
||||
@@ -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