Update location function

This commit is contained in:
Nguyen Ngo
2025-08-01 16:51:16 -04:00
parent fdd9f67f9a
commit c16632487d
3 changed files with 797 additions and 104 deletions
+132 -42
View File
@@ -74,9 +74,7 @@ class QRCode(db.Model):
qr_url = db.Column(db.String(255), unique=True, nullable=True) qr_url = db.Column(db.String(255), unique=True, nullable=True)
class AttendanceData(db.Model): class AttendanceData(db.Model):
""" """Enhanced attendance tracking model with location support"""
Enhanced attendance tracking model with comprehensive location support
"""
__tablename__ = 'attendance_data' __tablename__ = 'attendance_data'
# Existing fields # Existing fields
@@ -93,13 +91,13 @@ class AttendanceData(db.Model):
created_timestamp = db.Column(db.DateTime, default=datetime.utcnow) created_timestamp = db.Column(db.DateTime, default=datetime.utcnow)
updated_timestamp = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) updated_timestamp = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# NEW: Location tracking fields # LOCATION FIELDS - Add these if missing
latitude = db.Column(db.Float, nullable=True, comment='GPS latitude coordinate') latitude = db.Column(db.Float, nullable=True)
longitude = db.Column(db.Float, nullable=True, comment='GPS longitude coordinate') longitude = db.Column(db.Float, nullable=True)
accuracy = db.Column(db.Float, nullable=True, comment='GPS accuracy in meters') accuracy = db.Column(db.Float, nullable=True)
altitude = db.Column(db.Float, nullable=True, comment='GPS altitude in meters') altitude = db.Column(db.Float, nullable=True)
location_source = db.Column(db.String(50), default='manual', comment='Source: gps, network, manual') location_source = db.Column(db.String(50), default='manual')
address = db.Column(db.String(500), nullable=True, comment='Reverse geocoded address') address = db.Column(db.String(500), nullable=True)
# Relationships # Relationships
qr_code = db.relationship('QRCode', backref=db.backref('attendance_records', lazy='dynamic')) qr_code = db.relationship('QRCode', backref=db.backref('attendance_records', lazy='dynamic'))
@@ -1152,7 +1150,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):
"""Enhanced staff check-in submission with comprehensive location support""" """Enhanced staff check-in with proper location handling"""
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()
@@ -1166,17 +1164,26 @@ def qr_checkin(qr_url):
# Get form data # Get form data
employee_id = request.form.get('employee_id', '').strip() employee_id = request.form.get('employee_id', '').strip()
# Enhanced location data extraction # FIXED: Get location data with correct field names and validation
location_data = { latitude = request.form.get('latitude', '').strip()
'latitude': request.form.get('latitude', '').strip(), longitude = request.form.get('longitude', '').strip()
'longitude': request.form.get('longitude', '').strip(), accuracy = request.form.get('accuracy', '').strip()
'accuracy': request.form.get('accuracy', '').strip(), altitude = request.form.get('altitude', '').strip()
'altitude': request.form.get('altitude', '').strip(), location_source = request.form.get('location_source', 'manual').strip()
'location_source': request.form.get('locationSource', 'manual').strip(), address = request.form.get('address', '').strip()
'address': request.form.get('address', '').strip()
}
print(f"📍 Location data received: {location_data}") # CRITICAL DEBUG: Log all received form data
print(f"\n{'='*50}")
print(f"📥 QR CHECK-IN DATA RECEIVED:")
print(f"{'='*50}")
print(f"Employee ID: '{employee_id}'")
print(f"Latitude: '{latitude}' (type: {type(latitude)})")
print(f"Longitude: '{longitude}' (type: {type(longitude)})")
print(f"Accuracy: '{accuracy}' (type: {type(accuracy)})")
print(f"Altitude: '{altitude}' (type: {type(altitude)})")
print(f"Location Source: '{location_source}' (type: {type(location_source)})")
print(f"Address: '{address}' (type: {type(address)})")
print(f"{'='*50}\n")
if not employee_id: if not employee_id:
return jsonify({ return jsonify({
@@ -1185,7 +1192,7 @@ def qr_checkin(qr_url):
}), 400 }), 400
# Validate employee ID format # Validate employee ID format
if not re.match(r'^[A-Za-z0-9]{3,20}$', employee_id): if not re.match(r'^[A-Za-z0-9]{3,20}', employee_id):
return jsonify({ return jsonify({
'success': False, 'success': False,
'message': 'Invalid employee ID format. Use 3-20 alphanumeric characters.' 'message': 'Invalid employee ID format. Use 3-20 alphanumeric characters.'
@@ -1219,10 +1226,74 @@ 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()
# Process and validate location data # FIXED: Process location data with enhanced validation
processed_location = process_location_data(location_data) lat_value = None
lng_value = None
acc_value = None
alt_value = None
# Create enhanced attendance record # Process latitude
if latitude and latitude.strip() and latitude not in ['null', '', 'undefined']:
try:
lat_value = float(latitude)
if not (-90 <= lat_value <= 90):
print(f"⚠️ Invalid latitude range: {lat_value}")
lat_value = None
else:
print(f"✅ Valid latitude: {lat_value}")
except (ValueError, TypeError) as e:
print(f"⚠️ Latitude parsing error: {e}")
# Process longitude
if longitude and longitude.strip() and longitude not in ['null', '', 'undefined']:
try:
lng_value = float(longitude)
if not (-180 <= lng_value <= 180):
print(f"⚠️ Invalid longitude range: {lng_value}")
lng_value = None
else:
print(f"✅ Valid longitude: {lng_value}")
except (ValueError, TypeError) as e:
print(f"⚠️ Longitude parsing error: {e}")
# Process accuracy
if accuracy and accuracy.strip() and accuracy not in ['null', '', 'undefined']:
try:
acc_value = float(accuracy)
if acc_value < 0:
print(f"⚠️ Invalid accuracy (negative): {acc_value}")
acc_value = None
else:
print(f"✅ Valid accuracy: {acc_value}m")
except (ValueError, TypeError) as e:
print(f"⚠️ Accuracy parsing error: {e}")
# Process altitude
if altitude and altitude.strip() and altitude not in ['null', '', 'undefined']:
try:
alt_value = float(altitude)
print(f"✅ Valid altitude: {alt_value}m")
except (ValueError, TypeError) as e:
print(f"⚠️ Altitude parsing error: {e}")
# Validate location source
valid_sources = ['gps', 'network', 'manual']
if location_source not in valid_sources:
location_source = 'manual'
# Truncate address if too long
if address and len(address) > 500:
address = address[:500]
print(f"📊 PROCESSED LOCATION DATA:")
print(f" Latitude: {lat_value}")
print(f" Longitude: {lng_value}")
print(f" Accuracy: {acc_value}")
print(f" Altitude: {alt_value}")
print(f" Source: {location_source}")
print(f" Address: {address[:50]}..." if address and len(address) > 50 else f" Address: {address}")
# Create attendance record
attendance = AttendanceData( attendance = AttendanceData(
qr_code_id=qr_code.id, qr_code_id=qr_code.id,
employee_id=employee_id.upper(), employee_id=employee_id.upper(),
@@ -1233,19 +1304,37 @@ def qr_checkin(qr_url):
ip_address=client_ip, ip_address=client_ip,
location_name=qr_code.location, location_name=qr_code.location,
status='present', status='present',
# Location data # FIXED: Add location data directly to constructor
latitude=processed_location.get('latitude'), latitude=lat_value,
longitude=processed_location.get('longitude'), longitude=lng_value,
accuracy=processed_location.get('accuracy'), accuracy=acc_value,
altitude=processed_location.get('altitude'), altitude=alt_value,
location_source=processed_location.get('source', 'manual'), location_source=location_source,
address=processed_location.get('address') address=address
) )
print(f"💾 SAVING ATTENDANCE RECORD:")
print(f" Employee: {attendance.employee_id}")
print(f" Location: {attendance.location_name}")
print(f" GPS: {attendance.latitude}, {attendance.longitude}")
print(f" Accuracy: {attendance.accuracy}")
print(f" Source: {attendance.location_source}")
db.session.add(attendance) db.session.add(attendance)
db.session.commit() db.session.commit()
# Enhanced response with location info # VERIFICATION: Re-fetch the saved record to confirm data persistence
saved_record = AttendanceData.query.get(attendance.id)
has_location = saved_record.latitude is not None and saved_record.longitude is not None
print(f"✅ VERIFICATION - Record saved with ID: {saved_record.id}")
print(f"✅ Has location data: {has_location}")
if has_location:
print(f"✅ Saved coordinates: {saved_record.latitude}, {saved_record.longitude}")
print(f"✅ Saved accuracy: {saved_record.accuracy}")
print(f"✅ Saved source: {saved_record.location_source}")
# Enhanced response with location verification
response_data = { response_data = {
'success': True, 'success': True,
'message': 'Check-in successful!', 'message': 'Check-in successful!',
@@ -1255,22 +1344,23 @@ def qr_checkin(qr_url):
'event': qr_code.location_event, 'event': qr_code.location_event,
'check_in_time': attendance.check_in_time.strftime('%H:%M'), 'check_in_time': attendance.check_in_time.strftime('%H:%M'),
'check_in_date': attendance.check_in_date.strftime('%B %d, %Y'), 'check_in_date': attendance.check_in_date.strftime('%B %d, %Y'),
'has_location': attendance.has_location_data, 'has_location': has_location,
'location_info': { 'location_info': {
'coordinates': attendance.coordinates_display, 'coordinates': f"{saved_record.latitude:.6f}, {saved_record.longitude:.6f}" if has_location else "No GPS data",
'accuracy': f"±{attendance.accuracy:.0f}m" if attendance.accuracy else "Unknown", 'accuracy': f"±{saved_record.accuracy:.0f}m" if saved_record.accuracy else "Unknown",
'source': attendance.location_source.title(), 'source': saved_record.location_source.title() if saved_record.location_source else "Manual",
'address': attendance.address or "Not available" 'address': saved_record.address or "Not available"
} if attendance.has_location_data else None } if has_location else None
} }
} }
print(f"✅ Check-in successful for {employee_id.upper()} with location: {attendance.has_location_data}") print(f"📤 SENDING RESPONSE: {response_data['data']['has_location']} location data")
return jsonify(response_data) return jsonify(response_data)
except Exception as e: except Exception as e:
print(f"❌ Check-in error: {str(e)}") print(f"❌ CHECK-IN ERROR: {str(e)}")
import traceback
print(f"❌ TRACEBACK: {traceback.format_exc()}")
db.session.rollback() db.session.rollback()
return jsonify({ return jsonify({
'success': False, 'success': False,
+529
View File
@@ -0,0 +1,529 @@
#!/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()
+136 -62
View File
@@ -70,23 +70,41 @@ function setupEventListeners() {
document.addEventListener('visibilitychange', handleVisibilityChange); document.addEventListener('visibilitychange', handleVisibilityChange);
} }
// NEW: Initialize geolocation functionality // Initialize geolocation functionality
function initializeGeolocation() { function initializeGeolocation() {
console.log('📍 Initializing geolocation...'); console.log('📍 Initializing geolocation system...');
if (!navigator.geolocation) { if (!navigator.geolocation) {
console.warn(' Geolocation not supported'); console.log('⚠️ Geolocation not supported by this browser');
showLocationStatus('error', 'Location not supported by this browser'); showLocationStatus('error', 'Location services not supported');
return; return;
} }
console.log('✅ Geolocation supported'); console.log('✅ Geolocation API available');
// Check permissions first // Request location immediately
checkLocationPermission();
// Request location
requestUserLocation(); requestUserLocation();
// Set up continuous watching for better accuracy
if ('permissions' in navigator) {
navigator.permissions.query({ name: 'geolocation' }).then(function(result) {
console.log('📍 Geolocation permission status:', result.state);
if (result.state === 'granted') {
startLocationWatching();
}
result.onchange = function() {
console.log('📍 Geolocation permission changed to:', result.state);
if (result.state === 'granted') {
requestUserLocation();
startLocationWatching();
} else {
stopLocationWatching();
}
};
});
}
} }
// NEW: Check location permissions // NEW: Check location permissions
@@ -109,7 +127,7 @@ function checkLocationPermission() {
} }
} }
// NEW: Request user's current location // Request user's current location
function requestUserLocation() { function requestUserLocation() {
if (locationRequestActive) { if (locationRequestActive) {
console.log('⏭️ Location request already active'); console.log('⏭️ Location request already active');
@@ -142,83 +160,120 @@ function requestUserLocation() {
}, 16000); }, 16000);
} }
// NEW: Handle successful location retrieval // Ensure location form fields exist
function ensureLocationFormFields() {
const form = document.getElementById('checkinForm');
if (!form) {
console.log('⚠️ Check-in form not found');
return;
}
const locationFields = ['latitude', 'longitude', 'accuracy', 'altitude', 'location_source', 'address'];
locationFields.forEach(fieldName => {
if (!document.getElementById(fieldName)) {
const input = document.createElement('input');
input.type = 'hidden';
input.id = fieldName;
input.name = fieldName;
input.value = '';
form.appendChild(input);
console.log(`✅ Created hidden field: ${fieldName}`);
}
});
}
// Handle successful location retrieval
function handleLocationSuccess(position) { function handleLocationSuccess(position) {
locationRequestActive = false; locationRequestActive = false;
const coords = position.coords; const coords = position.coords;
console.log('✅ Location obtained:', coords); console.log('✅ Location obtained:', {
// Store location data
userLocation = {
latitude: coords.latitude, latitude: coords.latitude,
longitude: coords.longitude, longitude: coords.longitude,
accuracy: coords.accuracy, accuracy: coords.accuracy,
altitude: coords.altitude, altitude: coords.altitude,
timestamp: position.timestamp
});
// Validate coordinates
if (!coords.latitude || !coords.longitude) {
console.log('⚠️ Invalid coordinates received');
handleLocationError({ code: 2, message: 'Invalid coordinates' });
return;
}
// Store location data with validation
userLocation = {
latitude: Number(coords.latitude).toFixed(6), // Limit precision
longitude: Number(coords.longitude).toFixed(6),
accuracy: coords.accuracy ? Math.round(coords.accuracy) : null,
altitude: coords.altitude ? Math.round(coords.altitude) : null,
timestamp: position.timestamp, timestamp: position.timestamp,
source: 'gps', source: 'gps',
address: null address: null
}; };
// Update form fields console.log('💾 Stored location data:', userLocation);
// Update form fields immediately
updateLocationFormFields(); updateLocationFormFields();
// Update display // Update display
updateLocationDisplay(); updateLocationDisplay();
// Show success status // Show success status
const accuracyText = coords.accuracy ? Math.round(coords.accuracy) : 'unknown'; const accuracyText = coords.accuracy ? `±${Math.round(coords.accuracy)}m` : 'unknown';
const accuracyLevel = getAccuracyLevel(coords.accuracy); showLocationStatus('success', `Location captured (${accuracyText} accuracy)`);
showLocationStatus('success', // Try to get address
`Location captured (±${accuracyText}m - ${accuracyLevel} accuracy)`
);
console.log('📍 Location stored:', userLocation);
// Try to get address from coordinates
reverseGeocodeLocation(coords.latitude, coords.longitude); reverseGeocodeLocation(coords.latitude, coords.longitude);
// Start watching for better accuracy (optional)
startLocationWatching();
} }
// NEW: Handle location errors // Handle location errors
function handleLocationError(error) { function handleLocationError(error) {
locationRequestActive = false; locationRequestActive = false;
console.error('❌ Location error:', error);
let message = 'Unable to get location'; let message = 'Unable to get location';
let details = 'Check-in will work without location';
console.log('❌ Location error:', error);
switch(error.code) { switch(error.code) {
case error.PERMISSION_DENIED: case error.PERMISSION_DENIED:
message = 'Location access denied'; message = 'Location access denied - please enable in browser settings';
details = 'Enable location permission in browser settings';
break; break;
case error.POSITION_UNAVAILABLE: case error.POSITION_UNAVAILABLE:
message = 'Location unavailable'; message = 'Location unavailable - GPS signal weak';
details = 'GPS signal is weak or unavailable';
break; break;
case error.TIMEOUT: case error.TIMEOUT:
message = 'Location request timed out'; message = 'Location request timed out';
details = 'Try refreshing or check connection';
break; break;
default: default:
message = 'Location error occurred'; message = 'Location error occurred';
details = 'Please try again';
break;
} }
showLocationStatus('error', `${message} - ${details}`); showLocationStatus('error', `${message} - check-in will continue without location`);
// Set location source to manual
userLocation.source = 'manual'; userLocation.source = 'manual';
updateLocationFormFields(); updateLocationFormFields();
} }
// NEW: Start watching location for continuous updates // Enhanced form initialization
document.addEventListener('DOMContentLoaded', function() {
console.log('🚀 QR Destination page initialized with enhanced location tracking');
// Initialize the page
initializePage();
setupEventListeners();
startTimeUpdater();
// Initialize geolocation
initializeGeolocation();
// Add hidden form fields for location data
ensureLocationFormFields();
});
// Start watching location for continuous updates
function startLocationWatching() { function startLocationWatching() {
if (locationWatchId !== null) { if (locationWatchId !== null) {
console.log('👁️ Already watching location'); console.log('👁️ Already watching location');
@@ -257,25 +312,32 @@ function stopLocationWatching() {
} }
} }
// NEW: Update form fields with location data // Update form fields with location data
function updateLocationFormFields() { function updateLocationFormFields() {
const fields = { const fields = {
'latitude': userLocation.latitude || '', 'latitude': userLocation.latitude || '',
'longitude': userLocation.longitude || '', 'longitude': userLocation.longitude || '',
'accuracy': userLocation.accuracy || '', 'accuracy': userLocation.accuracy || '',
'altitude': userLocation.altitude || '', 'altitude': userLocation.altitude || '',
'locationSource': userLocation.source || 'manual', 'location_source': userLocation.source || 'manual', // FIXED: was 'locationSource'
'address': userLocation.address || '' 'address': userLocation.address || ''
}; };
// Update hidden form fields
Object.keys(fields).forEach(fieldId => { Object.keys(fields).forEach(fieldId => {
const field = document.getElementById(fieldId); let field = document.getElementById(fieldId);
if (field) { if (!field) {
field.value = fields[fieldId]; // Create hidden input if it doesn't exist
field = document.createElement('input');
field.type = 'hidden';
field.id = fieldId;
field.name = fieldId;
document.getElementById('checkinForm').appendChild(field);
} }
field.value = fields[fieldId];
}); });
console.log('📝 Updated form fields with location data'); console.log('📝 Updated form fields with location data:', fields);
} }
// NEW: Update location display // NEW: Update location display
@@ -549,24 +611,25 @@ function shakeInput(input) {
} }
function submitCheckin(employeeId) { function submitCheckin(employeeId) {
console.log('🚀 Submitting check-in for:', employeeId); if (isSubmitting) {
console.log('⏭️ Already submitting, ignoring duplicate request');
// NEW: Log location data being submitted return false;
const locationData = getCurrentLocationData(); }
console.log('📍 Location data:', locationData);
isSubmitting = true; isSubmitting = true;
updateSubmitButton(true); updateSubmitButton(true);
showStatusMessage('Processing check-in...', 'info'); hideStatusMessage();
// NEW: Ensure all location data is in the form console.log('📤 Starting check-in submission for:', employeeId);
console.log('📍 Current location data:', userLocation);
// Ensure location data is in the form
updateLocationFormFields(); updateLocationFormFields();
// Prepare form data // Prepare form data with CORRECT field names
const formData = new FormData(); const formData = new FormData();
formData.append('employee_id', employeeId); formData.append('employee_id', employeeId);
// NEW: Add location data to form submission
formData.append('latitude', userLocation.latitude || ''); formData.append('latitude', userLocation.latitude || '');
formData.append('longitude', userLocation.longitude || ''); formData.append('longitude', userLocation.longitude || '');
formData.append('accuracy', userLocation.accuracy || ''); formData.append('accuracy', userLocation.accuracy || '');
@@ -574,11 +637,17 @@ function submitCheckin(employeeId) {
formData.append('location_source', userLocation.source || 'manual'); formData.append('location_source', userLocation.source || 'manual');
formData.append('address', userLocation.address || ''); formData.append('address', userLocation.address || '');
// DEBUG: Log exactly what we're sending
console.log('📤 Form data being submitted:');
for (let [key, value] of formData.entries()) {
console.log(` ${key}: "${value}"`);
}
// Get the current URL for the check-in endpoint // Get the current URL for the check-in endpoint
const currentUrl = window.location.pathname; const currentUrl = window.location.pathname;
const checkinUrl = `${currentUrl}/checkin`; const checkinUrl = `${currentUrl}/checkin`;
console.log('📤 Submitting to:', checkinUrl); console.log('🎯 Submitting to URL:', checkinUrl);
fetch(checkinUrl, { fetch(checkinUrl, {
method: 'POST', method: 'POST',
@@ -587,14 +656,19 @@ function submitCheckin(employeeId) {
'X-Requested-With': 'XMLHttpRequest' 'X-Requested-With': 'XMLHttpRequest'
} }
}) })
.then(response => response.json()) .then(response => {
console.log('📡 Server response status:', response.status);
return response.json();
})
.then(data => { .then(data => {
isSubmitting = false; isSubmitting = false;
updateSubmitButton(false); updateSubmitButton(false);
console.log('📥 Server response:', data);
if (data.success) { if (data.success) {
showSuccessPage(data); showSuccessPage(data);
console.log('✅ Check-in successful:', data); console.log('✅ Check-in successful with location:', data.data?.has_location || false);
// Stop location watching after successful check-in // Stop location watching after successful check-in
stopLocationWatching(); stopLocationWatching();
@@ -607,7 +681,7 @@ function submitCheckin(employeeId) {
isSubmitting = false; isSubmitting = false;
updateSubmitButton(false); updateSubmitButton(false);
showStatusMessage('Network error. Please check your connection and try again.', 'error'); showStatusMessage('Network error. Please check your connection and try again.', 'error');
console.error('❌ Check-in error:', error); console.error('❌ Network error:', error);
}); });
} }