Updated location address accuracy
This commit is contained in:
@@ -663,10 +663,55 @@ def process_location_data(location_data):
|
|||||||
|
|
||||||
return processed
|
return processed
|
||||||
|
|
||||||
|
def reverse_geocode_coordinates(latitude, longitude):
|
||||||
|
"""
|
||||||
|
Convert GPS coordinates to human-readable address using reverse geocoding
|
||||||
|
Returns address string or None if failed
|
||||||
|
"""
|
||||||
|
if not latitude or not longitude:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(f"🌍 Reverse geocoding coordinates: {latitude}, {longitude}")
|
||||||
|
|
||||||
|
# Using Nominatim (OpenStreetMap) reverse geocoding service
|
||||||
|
url = "https://nominatim.openstreetmap.org/reverse"
|
||||||
|
params = {
|
||||||
|
'lat': latitude,
|
||||||
|
'lon': longitude,
|
||||||
|
'format': 'json',
|
||||||
|
'addressdetails': 1,
|
||||||
|
'zoom': 18 # High detail level
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'User-Agent': 'QR-Attendance-System/1.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.get(url, params=params, headers=headers, timeout=10)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if data and 'display_name' in data:
|
||||||
|
address = data['display_name']
|
||||||
|
print(f"✅ Reverse geocoded address: {address}")
|
||||||
|
return address
|
||||||
|
else:
|
||||||
|
print(f"⚠️ No address found for coordinates")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
print(f"⚠️ Reverse geocoding API returned status: {response.status_code}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Error in reverse geocoding: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
def process_location_data_enhanced(form_data):
|
def process_location_data_enhanced(form_data):
|
||||||
"""
|
"""
|
||||||
Enhanced processing of location data from form submission
|
Enhanced processing of location data from form submission
|
||||||
Validates and cleans location data for storage
|
Validates and cleans location data for storage, including reverse geocoding
|
||||||
"""
|
"""
|
||||||
processed = {
|
processed = {
|
||||||
'latitude': None,
|
'latitude': None,
|
||||||
@@ -707,13 +752,33 @@ def process_location_data_enhanced(form_data):
|
|||||||
alt = float(form_data['altitude'])
|
alt = float(form_data['altitude'])
|
||||||
processed['altitude'] = alt
|
processed['altitude'] = alt
|
||||||
|
|
||||||
# Process address (limit length for database storage)
|
# Process address - First check if address was provided
|
||||||
if form_data.get('address'):
|
if form_data.get('address'):
|
||||||
address = form_data['address'].strip()
|
address = form_data['address'].strip()
|
||||||
if address and address not in ['null', '', 'undefined']:
|
if address and address not in ['null', '', 'undefined']:
|
||||||
processed['address'] = address[:500] # Limit to 500 characters
|
# Check if the address is just coordinates (like "38.8104192000, -77.1850240000")
|
||||||
|
if re.match(r'^-?\d+\.\d+,?\s*-?\d+\.\d+$', address.replace(' ', '')):
|
||||||
|
print(f"🔍 Detected coordinate-format address: {address}")
|
||||||
|
# This is just coordinates, we need to reverse geocode
|
||||||
|
processed['address'] = None # Reset so reverse geocoding will trigger
|
||||||
|
else:
|
||||||
|
# This is a real address
|
||||||
|
processed['address'] = address[:500] # Limit to 500 characters
|
||||||
|
print(f"✅ Using provided address: {processed['address'][:100]}...")
|
||||||
|
|
||||||
print(f"📍 Processed location data:")
|
# CRITICAL: If we have coordinates but no real address, perform reverse geocoding
|
||||||
|
if (processed['latitude'] is not None and processed['longitude'] is not None
|
||||||
|
and not processed['address']):
|
||||||
|
print(f"🌍 Performing reverse geocoding for coordinates: {processed['latitude']}, {processed['longitude']}")
|
||||||
|
reverse_geocoded_address = reverse_geocode_coordinates(processed['latitude'], processed['longitude'])
|
||||||
|
if reverse_geocoded_address:
|
||||||
|
processed['address'] = reverse_geocoded_address[:500]
|
||||||
|
print(f"✅ Reverse geocoded address: {processed['address']}")
|
||||||
|
else:
|
||||||
|
print(f"⚠️ Could not reverse geocode coordinates, keeping coordinates as fallback")
|
||||||
|
processed['address'] = f"{processed['latitude']:.10f}, {processed['longitude']:.10f}"
|
||||||
|
|
||||||
|
print(f"📍 Final processed location data:")
|
||||||
print(f" Coordinates: {processed['latitude']}, {processed['longitude']}")
|
print(f" Coordinates: {processed['latitude']}, {processed['longitude']}")
|
||||||
print(f" GPS Accuracy: {processed['accuracy']}m")
|
print(f" GPS Accuracy: {processed['accuracy']}m")
|
||||||
print(f" Source: {processed['source']}")
|
print(f" Source: {processed['source']}")
|
||||||
@@ -1932,6 +1997,7 @@ def qr_checkin(qr_url):
|
|||||||
"""
|
"""
|
||||||
Enhanced staff check-in with location accuracy calculation
|
Enhanced staff check-in with location accuracy calculation
|
||||||
Allows multiple check-ins with minimum 30-minute intervals between them
|
Allows multiple check-ins with minimum 30-minute intervals between them
|
||||||
|
PRESERVES coordinate-to-address conversion functionality
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
print(f"\n🚀 STARTING ENHANCED CHECK-IN PROCESS")
|
print(f"\n🚀 STARTING ENHANCED CHECK-IN PROCESS")
|
||||||
@@ -1980,23 +2046,23 @@ def qr_checkin(qr_url):
|
|||||||
# Check if 30 minutes have passed since the last check-in
|
# Check if 30 minutes have passed since the last check-in
|
||||||
if recent_checkin_datetime > thirty_minutes_ago:
|
if recent_checkin_datetime > thirty_minutes_ago:
|
||||||
minutes_remaining = 30 - int((current_time - recent_checkin_datetime).total_seconds() / 60)
|
minutes_remaining = 30 - int((current_time - recent_checkin_datetime).total_seconds() / 60)
|
||||||
print(f"⚠️ Too soon for another submission for {employee_id}")
|
print(f"⚠️ Too soon for another check-in for {employee_id}")
|
||||||
print(f" Last submission: {recent_checkin.check_in_time.strftime('%H:%M')}")
|
print(f" Last check-in: {recent_checkin.check_in_time.strftime('%H:%M')}")
|
||||||
print(f" Minutes remaining: {minutes_remaining}")
|
print(f" Minutes remaining: {minutes_remaining}")
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'message': f'You can submit again in {minutes_remaining} minutes. Last submission was at {recent_checkin.check_in_time.strftime("%H:%M")}.'
|
'message': f'You can check in again in {minutes_remaining} minutes. Last check-in was at {recent_checkin.check_in_time.strftime("%H:%M")}.'
|
||||||
}), 400
|
}), 400
|
||||||
else:
|
else:
|
||||||
print(f"✅ 30-minute interval satisfied. Allowing new check-in for {employee_id}")
|
print(f"✅ 30-minute interval satisfied. Allowing new check-in for {employee_id}")
|
||||||
else:
|
else:
|
||||||
print(f"✅ First submission today for {employee_id}")
|
print(f"✅ First check-in today for {employee_id}")
|
||||||
|
|
||||||
# Process location data
|
# PRESERVED: Process location data with coordinate-to-address conversion
|
||||||
location_data = process_location_data_enhanced(request.form)
|
location_data = process_location_data_enhanced(request.form)
|
||||||
|
|
||||||
# Get device and network info
|
# PRESERVED: Get device and network info
|
||||||
user_agent_string = request.headers.get('User-Agent', '')
|
user_agent_string = request.headers.get('User-Agent', '')
|
||||||
device_info = detect_device_info(user_agent_string)
|
device_info = detect_device_info(user_agent_string)
|
||||||
client_ip = get_client_ip()
|
client_ip = get_client_ip()
|
||||||
@@ -2005,7 +2071,7 @@ def qr_checkin(qr_url):
|
|||||||
print(f"🌐 IP Address: {client_ip}")
|
print(f"🌐 IP Address: {client_ip}")
|
||||||
print(f"📍 Location Data: {location_data}")
|
print(f"📍 Location Data: {location_data}")
|
||||||
|
|
||||||
# Create attendance record
|
# PRESERVED: Create attendance record
|
||||||
print(f"\n💾 CREATING ATTENDANCE RECORD:")
|
print(f"\n💾 CREATING ATTENDANCE RECORD:")
|
||||||
|
|
||||||
attendance = AttendanceData(
|
attendance = AttendanceData(
|
||||||
@@ -2022,13 +2088,13 @@ def qr_checkin(qr_url):
|
|||||||
accuracy=location_data['accuracy'],
|
accuracy=location_data['accuracy'],
|
||||||
altitude=location_data['altitude'],
|
altitude=location_data['altitude'],
|
||||||
location_source=location_data['source'],
|
location_source=location_data['source'],
|
||||||
address=location_data['address'],
|
address=location_data['address'], # This now includes converted address
|
||||||
status='present'
|
status='present'
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"✅ Created base attendance record")
|
print(f"✅ Created base attendance record")
|
||||||
|
|
||||||
# Calculate location accuracy
|
# PRESERVED: Calculate location accuracy
|
||||||
print(f"\n🎯 CALCULATING LOCATION ACCURACY...")
|
print(f"\n🎯 CALCULATING LOCATION ACCURACY...")
|
||||||
location_accuracy = None
|
location_accuracy = None
|
||||||
|
|
||||||
@@ -2050,13 +2116,13 @@ def qr_checkin(qr_url):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Error in location accuracy calculation: {e}")
|
print(f"❌ Error in location accuracy calculation: {e}")
|
||||||
|
|
||||||
# Save to database
|
# PRESERVED: Save to database
|
||||||
try:
|
try:
|
||||||
db.session.add(attendance)
|
db.session.add(attendance)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
print(f"✅ Successfully saved attendance record with ID: {attendance.id}")
|
print(f"✅ Successfully saved attendance record with ID: {attendance.id}")
|
||||||
|
|
||||||
# Count total check-ins for today for this employee at this location
|
# NEW: Count total check-ins for today for this employee at this location
|
||||||
today_checkin_count = AttendanceData.query.filter_by(
|
today_checkin_count = AttendanceData.query.filter_by(
|
||||||
qr_code_id=qr_code.id,
|
qr_code_id=qr_code.id,
|
||||||
employee_id=employee_id.upper(),
|
employee_id=employee_id.upper(),
|
||||||
@@ -2073,7 +2139,7 @@ def qr_checkin(qr_url):
|
|||||||
'message': 'Database error occurred.'
|
'message': 'Database error occurred.'
|
||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
# Return success response
|
# ENHANCED: Return success response with sequence information
|
||||||
response_data = {
|
response_data = {
|
||||||
'success': True,
|
'success': True,
|
||||||
'message': f'Check-in successful! {checkin_sequence_text} for today.',
|
'message': f'Check-in successful! {checkin_sequence_text} for today.',
|
||||||
@@ -2101,6 +2167,7 @@ def qr_checkin(qr_url):
|
|||||||
print(f" Employee: {attendance.employee_id}")
|
print(f" Employee: {attendance.employee_id}")
|
||||||
print(f" Time: {attendance.check_in_time}")
|
print(f" Time: {attendance.check_in_time}")
|
||||||
print(f" Location: {attendance.location_name}")
|
print(f" Location: {attendance.location_name}")
|
||||||
|
print(f" Address: {attendance.address}")
|
||||||
print(f" Today's count: {today_checkin_count}")
|
print(f" Today's count: {today_checkin_count}")
|
||||||
|
|
||||||
return jsonify(response_data), 200
|
return jsonify(response_data), 200
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const translations = {
|
|||||||
error: "Error en el registro. Por favor intente de nuevo.",
|
error: "Error en el registro. Por favor intente de nuevo.",
|
||||||
duplicate: "Ya se ha registrado hoy.",
|
duplicate: "Ya se ha registrado hoy.",
|
||||||
tooSoon: "Por favor espere antes de registrarse nuevamente.",
|
tooSoon: "Por favor espere antes de registrarse nuevamente.",
|
||||||
multipleSuccess: "Submitted successful!!",
|
multipleSuccess: "Submitted successfully!!",
|
||||||
invalidId: "Por favor ingrese un ID de empleado válido.",
|
invalidId: "Por favor ingrese un ID de empleado válido.",
|
||||||
locationError: "No se pudo obtener datos de ubicación.",
|
locationError: "No se pudo obtener datos de ubicación.",
|
||||||
networkError: "Error de red. Verifique su conexión.",
|
networkError: "Error de red. Verifique su conexión.",
|
||||||
@@ -225,7 +225,7 @@ function handleCheckinResponse(data) {
|
|||||||
|
|
||||||
// ENHANCED SUCCESS HANDLING WITH MULTIPLE CHECK-IN INFO
|
// ENHANCED SUCCESS HANDLING WITH MULTIPLE CHECK-IN INFO
|
||||||
function handleCheckinSuccess(data) {
|
function handleCheckinSuccess(data) {
|
||||||
console.log("✅ Submitted successful!");
|
console.log("✅ Submitted successfully!");
|
||||||
|
|
||||||
const responseData = data.data || data || {};
|
const responseData = data.data || data || {};
|
||||||
const checkinCount = responseData.checkin_count_today || 1;
|
const checkinCount = responseData.checkin_count_today || 1;
|
||||||
@@ -515,9 +515,32 @@ function handleLocationError(error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function reverseGeocode(lat, lng) {
|
function reverseGeocode(lat, lng) {
|
||||||
// This would typically use a geocoding service
|
console.log(`🌍 Starting reverse geocoding for: ${lat}, ${lng}`);
|
||||||
// For now, just set a placeholder
|
|
||||||
userLocation.address = `${lat.toFixed(10)}, ${lng.toFixed(10)}`;
|
// Using Nominatim (OpenStreetMap) reverse geocoding service
|
||||||
|
const url = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=json&addressdetails=1&zoom=18`;
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"User-Agent": "QR-Attendance-System/1.0",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data && data.display_name) {
|
||||||
|
userLocation.address = data.display_name;
|
||||||
|
console.log(`✅ Reverse geocoded address: ${userLocation.address}`);
|
||||||
|
} else {
|
||||||
|
console.log(`⚠️ No address found, using coordinates as fallback`);
|
||||||
|
userLocation.address = `${lat.toFixed(10)}, ${lng.toFixed(10)}`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(`❌ Reverse geocoding error:`, error);
|
||||||
|
// Fallback to coordinates if reverse geocoding fails
|
||||||
|
userLocation.address = `${lat.toFixed(10)}, ${lng.toFixed(10)}`;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Language functionality remains unchanged
|
// Language functionality remains unchanged
|
||||||
|
|||||||
+649
-566
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user