minor changes
This commit is contained in:
@@ -173,6 +173,7 @@ class AttendanceData(db.Model):
|
|||||||
'location_source': self.location_source
|
'location_source': self.location_source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Utility functions
|
||||||
def get_coordinates_from_address(address):
|
def get_coordinates_from_address(address):
|
||||||
"""
|
"""
|
||||||
Get latitude and longitude from address using geocoding service
|
Get latitude and longitude from address using geocoding service
|
||||||
@@ -213,110 +214,6 @@ def get_coordinates_from_address(address):
|
|||||||
print(f"❌ Error geocoding address '{address}': {e}")
|
print(f"❌ Error geocoding address '{address}': {e}")
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
'''
|
|
||||||
def get_coordinates_from_address(address):
|
|
||||||
"""
|
|
||||||
Enhanced geocoding function to get latitude and longitude from address
|
|
||||||
Returns (lat, lng, accuracy) tuple or (None, None, None) if failed
|
|
||||||
"""
|
|
||||||
if not address or address.strip() == '':
|
|
||||||
return None, None, None
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Using Nominatim (OpenStreetMap) geocoding service
|
|
||||||
url = "https://nominatim.openstreetmap.org/search"
|
|
||||||
params = {
|
|
||||||
'q': address,
|
|
||||||
'format': 'json',
|
|
||||||
'limit': 1,
|
|
||||||
'addressdetails': 1
|
|
||||||
}
|
|
||||||
|
|
||||||
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 len(data) > 0:
|
|
||||||
result = data[0]
|
|
||||||
lat = float(result['lat'])
|
|
||||||
lng = float(result['lon'])
|
|
||||||
|
|
||||||
# Determine accuracy based on result type
|
|
||||||
place_type = result.get('type', 'unknown')
|
|
||||||
osm_type = result.get('osm_type', 'unknown')
|
|
||||||
|
|
||||||
if place_type in ['house', 'building'] or osm_type == 'way':
|
|
||||||
accuracy = 'high'
|
|
||||||
elif place_type in ['neighbourhood', 'suburb', 'quarter']:
|
|
||||||
accuracy = 'medium'
|
|
||||||
else:
|
|
||||||
accuracy = 'low'
|
|
||||||
|
|
||||||
print(f"✅ Geocoded address: {address}")
|
|
||||||
print(f" Coordinates: {lat:.6f}, {lng:.6f}")
|
|
||||||
print(f" Accuracy: {accuracy} ({place_type})")
|
|
||||||
|
|
||||||
return lat, lng, accuracy
|
|
||||||
|
|
||||||
print(f"⚠️ No geocoding results for address: {address}")
|
|
||||||
return None, None, None
|
|
||||||
|
|
||||||
except requests.RequestException as e:
|
|
||||||
print(f"❌ Geocoding request error: {e}")
|
|
||||||
return None, None, None
|
|
||||||
except (ValueError, KeyError) as e:
|
|
||||||
print(f"❌ Geocoding parsing error: {e}")
|
|
||||||
return None, None, None
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Unexpected geocoding error: {e}")
|
|
||||||
return None, None, None
|
|
||||||
'''
|
|
||||||
'''
|
|
||||||
def get_coordinates_from_address(address):
|
|
||||||
"""
|
|
||||||
Get latitude and longitude from address using geocoding service
|
|
||||||
Returns (lat, lng) tuple or (None, None) if failed
|
|
||||||
"""
|
|
||||||
if not address or address.strip() == '':
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Using Nominatim/OpenStreetMap for free geocoding
|
|
||||||
url = "https://nominatim.openstreetmap.org/search"
|
|
||||||
params = {
|
|
||||||
'q': address,
|
|
||||||
'format': 'json',
|
|
||||||
'limit': 1,
|
|
||||||
'addressdetails': 1
|
|
||||||
}
|
|
||||||
|
|
||||||
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 len(data) > 0:
|
|
||||||
lat = float(data[0]['lat'])
|
|
||||||
lng = float(data[0]['lon'])
|
|
||||||
print(f"✅ Geocoded address '{address[:50]}...' to coordinates: {lat}, {lng}")
|
|
||||||
return lat, lng
|
|
||||||
|
|
||||||
print(f"⚠️ Could not geocode address: {address}")
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Error geocoding address '{address}': {e}")
|
|
||||||
return None, None
|
|
||||||
'''
|
|
||||||
|
|
||||||
def get_coordinates_from_address_enhanced(address):
|
def get_coordinates_from_address_enhanced(address):
|
||||||
"""
|
"""
|
||||||
Enhanced geocoding function with better error handling
|
Enhanced geocoding function with better error handling
|
||||||
@@ -681,42 +578,6 @@ def calculate_distance_miles(lat1, lng1, lat2, lng2):
|
|||||||
print(f"❌ Error calculating distance: {e}")
|
print(f"❌ Error calculating distance: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def calculate_location_accuracy(qr_address, checkin_address, checkin_lat=None, checkin_lng=None):
|
|
||||||
"""
|
|
||||||
Calculate location accuracy by comparing QR code address with check-in location
|
|
||||||
Returns distance in miles between the two locations
|
|
||||||
"""
|
|
||||||
print(f"\n📍 CALCULATING LOCATION ACCURACY:")
|
|
||||||
print(f" QR Address: {qr_address}")
|
|
||||||
print(f" Check-in Address: {checkin_address}")
|
|
||||||
print(f" Check-in Coordinates: {checkin_lat}, {checkin_lng}")
|
|
||||||
|
|
||||||
# Get QR code coordinates from address
|
|
||||||
qr_lat, qr_lng = get_coordinates_from_address(qr_address)
|
|
||||||
|
|
||||||
if qr_lat is None or qr_lng is None:
|
|
||||||
print(f"⚠️ Could not geocode QR address, cannot calculate accuracy")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Use check-in coordinates if available, otherwise geocode check-in address
|
|
||||||
if checkin_lat is not None and checkin_lng is not None:
|
|
||||||
checkin_coords_lat, checkin_coords_lng = checkin_lat, checkin_lng
|
|
||||||
print(f"✅ Using GPS coordinates for check-in location")
|
|
||||||
else:
|
|
||||||
checkin_coords_lat, checkin_coords_lng = get_coordinates_from_address(checkin_address)
|
|
||||||
if checkin_coords_lat is None or checkin_coords_lng is None:
|
|
||||||
print(f"⚠️ Could not geocode check-in address, cannot calculate accuracy")
|
|
||||||
return None
|
|
||||||
print(f"✅ Using geocoded coordinates for check-in address")
|
|
||||||
|
|
||||||
# Calculate distance
|
|
||||||
distance = calculate_distance_miles(qr_lat, qr_lng, checkin_coords_lat, checkin_coords_lng)
|
|
||||||
|
|
||||||
if distance is not None:
|
|
||||||
print(f"✅ Location accuracy calculated: {distance} miles")
|
|
||||||
|
|
||||||
return distance
|
|
||||||
|
|
||||||
def get_location_accuracy_level(location_accuracy):
|
def get_location_accuracy_level(location_accuracy):
|
||||||
"""Get human-readable location accuracy level based on distance"""
|
"""Get human-readable location accuracy level based on distance"""
|
||||||
if not location_accuracy:
|
if not location_accuracy:
|
||||||
@@ -750,7 +611,7 @@ def get_location_accuracy_level_enhanced(location_accuracy):
|
|||||||
return 'poor'
|
return 'poor'
|
||||||
else: # Greater than 1 mile
|
else: # Greater than 1 mile
|
||||||
return 'very_poor'
|
return 'very_poor'
|
||||||
'''
|
|
||||||
def process_location_data(location_data):
|
def process_location_data(location_data):
|
||||||
"""
|
"""
|
||||||
Process and validate location data from form
|
Process and validate location data from form
|
||||||
@@ -800,57 +661,7 @@ def process_location_data(location_data):
|
|||||||
print(f"⚠️ Error processing location data: {e}")
|
print(f"⚠️ Error processing location data: {e}")
|
||||||
|
|
||||||
return processed
|
return processed
|
||||||
'''
|
|
||||||
def process_location_data(location_data):
|
|
||||||
"""
|
|
||||||
Process and validate location data from form
|
|
||||||
Returns clean location data or None values for invalid data
|
|
||||||
"""
|
|
||||||
processed = {
|
|
||||||
'latitude': None,
|
|
||||||
'longitude': None,
|
|
||||||
'accuracy': None,
|
|
||||||
'altitude': None,
|
|
||||||
'source': location_data.get('location_source', 'manual'),
|
|
||||||
'address': location_data.get('address', '')[:500] if location_data.get('address') else None
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Process latitude
|
|
||||||
if location_data.get('latitude') and location_data['latitude'] not in ['null', '']:
|
|
||||||
lat = float(location_data['latitude'])
|
|
||||||
if -90 <= lat <= 90: # Valid latitude range
|
|
||||||
processed['latitude'] = lat
|
|
||||||
else:
|
|
||||||
print(f"⚠️ Invalid latitude: {lat}")
|
|
||||||
|
|
||||||
# Process longitude
|
|
||||||
if location_data.get('longitude') and location_data['longitude'] not in ['null', '']:
|
|
||||||
lng = float(location_data['longitude'])
|
|
||||||
if -180 <= lng <= 180: # Valid longitude range
|
|
||||||
processed['longitude'] = lng
|
|
||||||
else:
|
|
||||||
print(f"⚠️ Invalid longitude: {lng}")
|
|
||||||
|
|
||||||
# Process accuracy
|
|
||||||
if location_data.get('accuracy') and location_data['accuracy'] not in ['null', '']:
|
|
||||||
acc = float(location_data['accuracy'])
|
|
||||||
if acc >= 0: # Accuracy should be positive
|
|
||||||
processed['accuracy'] = acc
|
|
||||||
else:
|
|
||||||
print(f"⚠️ Invalid accuracy: {acc}")
|
|
||||||
|
|
||||||
# Process altitude
|
|
||||||
if location_data.get('altitude') and location_data['altitude'] not in ['null', '']:
|
|
||||||
alt = float(location_data['altitude'])
|
|
||||||
# Altitude can be negative (below sea level)
|
|
||||||
processed['altitude'] = alt
|
|
||||||
|
|
||||||
except (ValueError, TypeError) as e:
|
|
||||||
print(f"⚠️ Error processing location data: {e}")
|
|
||||||
|
|
||||||
return processed
|
|
||||||
'''
|
|
||||||
def process_location_data_enhanced(form_data):
|
def process_location_data_enhanced(form_data):
|
||||||
"""
|
"""
|
||||||
Enhanced location data processing with better validation and error handling
|
Enhanced location data processing with better validation and error handling
|
||||||
@@ -909,132 +720,7 @@ def process_location_data_enhanced(form_data):
|
|||||||
print(f"⚠️ Error in enhanced location data processing: {e}")
|
print(f"⚠️ Error in enhanced location data processing: {e}")
|
||||||
|
|
||||||
return processed
|
return processed
|
||||||
'''
|
|
||||||
def process_location_data_enhanced(form_data):
|
|
||||||
"""
|
|
||||||
Enhanced location data processing with better validation and error handling
|
|
||||||
"""
|
|
||||||
processed = {
|
|
||||||
'latitude': None,
|
|
||||||
'longitude': None,
|
|
||||||
'accuracy': None,
|
|
||||||
'altitude': None,
|
|
||||||
'source': form_data.get('location_source', 'manual'),
|
|
||||||
'address': None
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Enhanced latitude validation
|
|
||||||
if form_data.get('latitude') and form_data['latitude'] not in ['null', '', 'undefined']:
|
|
||||||
lat = float(form_data['latitude'])
|
|
||||||
if -90 <= lat <= 90:
|
|
||||||
processed['latitude'] = round(lat, 6) # 6 decimal precision
|
|
||||||
else:
|
|
||||||
print(f"⚠️ Invalid latitude range: {lat}")
|
|
||||||
|
|
||||||
# Enhanced longitude validation
|
|
||||||
if form_data.get('longitude') and form_data['longitude'] not in ['null', '', 'undefined']:
|
|
||||||
lng = float(form_data['longitude'])
|
|
||||||
if -180 <= lng <= 180:
|
|
||||||
processed['longitude'] = round(lng, 6) # 6 decimal precision
|
|
||||||
else:
|
|
||||||
print(f"⚠️ Invalid longitude range: {lng}")
|
|
||||||
|
|
||||||
# Enhanced accuracy validation
|
|
||||||
if form_data.get('accuracy') and form_data['accuracy'] not in ['null', '', 'undefined']:
|
|
||||||
acc = float(form_data['accuracy'])
|
|
||||||
if 0 <= acc <= 50000: # Reasonable accuracy range in meters
|
|
||||||
processed['accuracy'] = round(acc, 1)
|
|
||||||
else:
|
|
||||||
print(f"⚠️ Invalid accuracy value: {acc}")
|
|
||||||
|
|
||||||
# Enhanced altitude validation
|
|
||||||
if form_data.get('altitude') and form_data['altitude'] not in ['null', '', 'undefined']:
|
|
||||||
alt = float(form_data['altitude'])
|
|
||||||
if -1000 <= alt <= 10000: # Reasonable altitude range in meters
|
|
||||||
processed['altitude'] = round(alt, 1)
|
|
||||||
else:
|
|
||||||
print(f"⚠️ Invalid altitude value: {alt}")
|
|
||||||
|
|
||||||
# Enhanced address processing
|
|
||||||
if form_data.get('address'):
|
|
||||||
address = form_data['address'].strip()
|
|
||||||
if len(address) > 0:
|
|
||||||
processed['address'] = address[:500] # Limit to 500 characters
|
|
||||||
|
|
||||||
print(f"✅ Enhanced location data processed: {processed}")
|
|
||||||
|
|
||||||
except (ValueError, TypeError) as e:
|
|
||||||
print(f"⚠️ Error in enhanced location data processing: {e}")
|
|
||||||
|
|
||||||
return processed
|
|
||||||
'''
|
|
||||||
def migrate_to_enhanced_location_accuracy():
|
|
||||||
"""
|
|
||||||
Migration function to recalculate all existing records with enhanced accuracy
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
print("🔄 Starting enhanced location accuracy migration...")
|
|
||||||
|
|
||||||
# Get all records that need recalculation
|
|
||||||
records = db.session.execute(text("""
|
|
||||||
SELECT ad.id, qc.location_address, ad.address, ad.latitude, ad.longitude, ad.location_accuracy
|
|
||||||
FROM attendance_data ad
|
|
||||||
LEFT JOIN qr_codes qc ON ad.qr_code_id = qc.id
|
|
||||||
WHERE qc.location_address IS NOT NULL
|
|
||||||
""")).fetchall()
|
|
||||||
|
|
||||||
print(f"📊 Found {len(records)} records to process")
|
|
||||||
|
|
||||||
updated_count = 0
|
|
||||||
improved_count = 0
|
|
||||||
|
|
||||||
for record in records:
|
|
||||||
try:
|
|
||||||
# Calculate enhanced location accuracy
|
|
||||||
new_accuracy = calculate_location_accuracy_enhanced(
|
|
||||||
qr_address=record.location_address,
|
|
||||||
checkin_address=record.address,
|
|
||||||
checkin_lat=record.latitude,
|
|
||||||
checkin_lng=record.longitude
|
|
||||||
)
|
|
||||||
|
|
||||||
if new_accuracy is not None:
|
|
||||||
# Update the record
|
|
||||||
db.session.execute(text("""
|
|
||||||
UPDATE attendance_data
|
|
||||||
SET location_accuracy = :accuracy
|
|
||||||
WHERE id = :record_id
|
|
||||||
"""), {
|
|
||||||
'accuracy': new_accuracy,
|
|
||||||
'record_id': record.id
|
|
||||||
})
|
|
||||||
|
|
||||||
updated_count += 1
|
|
||||||
|
|
||||||
# Check if this is an improvement
|
|
||||||
if record.location_accuracy is None or abs(new_accuracy - (record.location_accuracy or 0)) > 0.001:
|
|
||||||
improved_count += 1
|
|
||||||
print(f" ✅ Updated record {record.id}: {record.location_accuracy} → {new_accuracy:.4f} miles")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ⚠️ Error processing record {record.id}: {e}")
|
|
||||||
|
|
||||||
# Commit all changes
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
print(f"✅ Enhanced migration completed!")
|
|
||||||
print(f" 📊 Records processed: {len(records)}")
|
|
||||||
print(f" ✅ Records updated: {updated_count}")
|
|
||||||
print(f" 📈 Records improved: {improved_count}")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Enhanced migration failed: {e}")
|
|
||||||
db.session.rollback()
|
|
||||||
return False
|
|
||||||
'''
|
|
||||||
def migrate_to_enhanced_location_accuracy():
|
def migrate_to_enhanced_location_accuracy():
|
||||||
"""
|
"""
|
||||||
Migration function to recalculate all existing records with enhanced accuracy
|
Migration function to recalculate all existing records with enhanced accuracy
|
||||||
|
|||||||
Reference in New Issue
Block a user