Files
GOV_QR_Codes_Management/location_accuracy_utils.py
T
2025-08-04 17:29:48 -04:00

372 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Location Accuracy Database Utilities
Additional utility functions for managing location accuracy data
in your attendance system database.
Usage:
python location_accuracy_utils.py [command] [options]
Commands:
check-schema Verify database schema for location accuracy
add-field Add location_accuracy field to attendance_data table
stats Show location accuracy statistics
export-report Export detailed location accuracy report
verify-data Verify data integrity for location accuracy
Author: Attendance System Enhancement Team
"""
import sys
import os
import csv
from datetime import datetime, date
import json
# Add your app directory to path
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
try:
from app import app, db, AttendanceData, QRCode
from app import get_location_accuracy_level_enhanced
from sqlalchemy import text, inspect
except ImportError as e:
print(f"❌ Error importing modules: {e}")
sys.exit(1)
class LocationAccuracyUtils:
"""Utility class for location accuracy database management"""
def __init__(self):
self.app = app
def check_database_schema(self):
"""Check if the database schema supports location accuracy"""
print("🔍 CHECKING DATABASE SCHEMA")
print("=" * 50)
with self.app.app_context():
inspector = inspect(db.engine)
# Check attendance_data table
try:
columns = inspector.get_columns('attendance_data')
column_names = [col['name'] for col in columns]
print("📋 Attendance Data Table Columns:")
required_fields = [
'id', 'qr_code_id', 'employee_id', 'check_in_date', 'check_in_time',
'latitude', 'longitude', 'address', 'location_accuracy'
]
for field in required_fields:
status = "✅" if field in column_names else "❌"
print(f" {status} {field}")
# Check for location_accuracy field specifically
has_accuracy_field = 'location_accuracy' in column_names
if has_accuracy_field:
# Get field details
accuracy_col = next((col for col in columns if col['name'] == 'location_accuracy'), None)
if accuracy_col:
print(f"\n📊 location_accuracy field details:")
print(f" Type: {accuracy_col['type']}")
print(f" Nullable: {accuracy_col['nullable']}")
print(f" Default: {accuracy_col.get('default', 'None')}")
return has_accuracy_field
except Exception as e:
print(f"❌ Error checking schema: {e}")
return False
def add_location_accuracy_field(self):
"""Add location_accuracy field to attendance_data table if missing"""
print("🔧 ADDING LOCATION ACCURACY FIELD")
print("=" * 50)
with self.app.app_context():
try:
# Check if field already exists
if self.check_database_schema():
print("️ location_accuracy field already exists")
return True
print(" Adding location_accuracy field...")
# Add the field
db.session.execute(text("""
ALTER TABLE attendance_data
ADD COLUMN location_accuracy FLOAT
"""))
db.session.commit()
print("✅ Successfully added location_accuracy field")
# Verify addition
if self.check_database_schema():
print("✅ Field addition verified")
return True
else:
print("❌ Field addition verification failed")
return False
except Exception as e:
print(f"❌ Error adding field: {e}")
db.session.rollback()
return False
def show_location_accuracy_stats(self):
"""Show comprehensive statistics about location accuracy data"""
print("📊 LOCATION ACCURACY STATISTICS")
print("=" * 60)
with self.app.app_context():
try:
# Basic counts
total_records = db.session.query(AttendanceData).count()
records_with_location = db.session.query(AttendanceData).filter(
AttendanceData.latitude.isnot(None),
AttendanceData.longitude.isnot(None)
).count()
records_with_accuracy = db.session.query(AttendanceData).filter(
AttendanceData.location_accuracy.isnot(None)
).count()
print(f"Total Records: {total_records:,}")
print(f"Records with GPS Data: {records_with_location:,}")
print(f"Records with Accuracy: {records_with_accuracy:,}")
if total_records > 0:
gps_percentage = (records_with_location / total_records) * 100
accuracy_percentage = (records_with_accuracy / total_records) * 100
print(f"GPS Coverage: {gps_percentage:.1f}%")
print(f"Accuracy Coverage: {accuracy_percentage:.1f}%")
# Accuracy level distribution
if records_with_accuracy > 0:
print("\n📈 ACCURACY LEVEL DISTRIBUTION")
print("-" * 40)
accuracy_records = db.session.query(AttendanceData.location_accuracy).filter(
AttendanceData.location_accuracy.isnot(None)
).all()
levels = {}
total_distance = 0
for record in accuracy_records:
accuracy = record[0]
level = get_location_accuracy_level_enhanced(accuracy)
levels[level] = levels.get(level, 0) + 1
total_distance += accuracy
for level in ['excellent', 'very_good', 'good', 'fair', 'poor', 'very_poor']:
count = levels.get(level, 0)
percentage = (count / records_with_accuracy) * 100 if records_with_accuracy > 0 else 0
print(f"{level.replace('_', ' ').title():12} {count:6,} ({percentage:5.1f}%)")
avg_accuracy = total_distance / len(accuracy_records)
print(f"\nAverage Distance: {avg_accuracy:.4f} miles")
# Recent activity
print("\n📅 RECENT ACTIVITY (Last 7 Days)")
print("-" * 40)
seven_days_ago = date.today().replace(day=date.today().day - 7) if date.today().day > 7 else date.today().replace(month=date.today().month - 1, day=date.today().day + 30 - 7)
recent_records = db.session.query(AttendanceData).filter(
AttendanceData.check_in_date >= seven_days_ago
).count()
recent_with_accuracy = db.session.query(AttendanceData).filter(
AttendanceData.check_in_date >= seven_days_ago,
AttendanceData.location_accuracy.isnot(None)
).count()
print(f"Recent Check-ins: {recent_records:,}")
print(f"Recent with Accuracy: {recent_with_accuracy:,}")
if recent_records > 0:
recent_percentage = (recent_with_accuracy / recent_records) * 100
print(f"Recent Accuracy Rate: {recent_percentage:.1f}%")
except Exception as e:
print(f"❌ Error generating statistics: {e}")
def export_location_accuracy_report(self, output_file=None):
"""Export detailed location accuracy report to CSV"""
if not output_file:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = f"location_accuracy_report_{timestamp}.csv"
print(f"📄 EXPORTING LOCATION ACCURACY REPORT")
print(f"Output File: {output_file}")
print("=" * 60)
with self.app.app_context():
try:
# Query all records with their QR code information
query = db.session.query(AttendanceData, QRCode).join(
QRCode, AttendanceData.qr_code_id == QRCode.id
).order_by(AttendanceData.check_in_date.desc(), AttendanceData.check_in_time.desc())
records = query.all()
print(f"Found {len(records)} records to export")
# Write CSV file
with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = [
'attendance_id', 'employee_id', 'check_in_date', 'check_in_time',
'location_name', 'qr_address', 'checkin_address',
'checkin_latitude', 'checkin_longitude', 'gps_accuracy_meters',
'location_accuracy_miles', 'accuracy_level',
'has_gps_data', 'has_location_accuracy', 'device_info'
]
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for attendance, qr_code in records:
accuracy_level = 'unknown'
if attendance.location_accuracy:
accuracy_level = get_location_accuracy_level_enhanced(attendance.location_accuracy)
writer.writerow({
'attendance_id': attendance.id,
'employee_id': attendance.employee_id,
'check_in_date': attendance.check_in_date.isoformat(),
'check_in_time': attendance.check_in_time.isoformat(),
'location_name': attendance.location_name,
'qr_address': qr_code.location_address,
'checkin_address': attendance.address or 'Not captured',
'checkin_latitude': attendance.latitude,
'checkin_longitude': attendance.longitude,
'gps_accuracy_meters': attendance.accuracy,
'location_accuracy_miles': attendance.location_accuracy,
'accuracy_level': accuracy_level,
'has_gps_data': attendance.latitude is not None and attendance.longitude is not None,
'has_location_accuracy': attendance.location_accuracy is not None,
'device_info': attendance.device_info or 'Unknown'
})
print(f"✅ Report exported successfully to {output_file}")
return output_file
except Exception as e:
print(f"❌ Error exporting report: {e}")
return None
def verify_data_integrity(self):
"""Verify data integrity for location accuracy calculations"""
print("🔍 VERIFYING DATA INTEGRITY")
print("=" * 50)
with self.app.app_context():
try:
issues = []
# Check for records with coordinates but no address
no_address_with_coords = db.session.query(AttendanceData).filter(
AttendanceData.latitude.isnot(None),
AttendanceData.longitude.isnot(None),
AttendanceData.address.is_(None)
).count()
if no_address_with_coords > 0:
issues.append(f"{no_address_with_coords} records have GPS coordinates but no address")
# Check for invalid coordinates
invalid_coords = db.session.query(AttendanceData).filter(
db.or_(
AttendanceData.latitude < -90,
AttendanceData.latitude > 90,
AttendanceData.longitude < -180,
AttendanceData.longitude > 180
)
).count()
if invalid_coords > 0:
issues.append(f"{invalid_coords} records have invalid GPS coordinates")
# Check for QR codes without addresses
qr_no_address = db.session.query(QRCode).filter(
db.or_(
QRCode.location_address.is_(None),
QRCode.location_address == ''
)
).count()
if qr_no_address > 0:
issues.append(f"{qr_no_address} QR codes have no address defined")
# Check for impossible accuracy values
extreme_accuracy = db.session.query(AttendanceData).filter(
db.or_(
AttendanceData.location_accuracy < 0,
AttendanceData.location_accuracy > 1000 # More than 1000 miles seems extreme
)
).count()
if extreme_accuracy > 0:
issues.append(f"{extreme_accuracy} records have extreme location accuracy values")
# Report results
if issues:
print("⚠️ Data integrity issues found:")
for issue in issues:
print(f" • {issue}")
else:
print("✅ No data integrity issues found")
return len(issues) == 0
except Exception as e:
print(f"❌ Error during verification: {e}")
return False
def main():
"""Main entry point for utility commands"""
if len(sys.argv) < 2:
print("""
Usage: python location_accuracy_utils.py [command]
Commands:
check-schema Check database schema
add-field Add location_accuracy field
stats Show statistics
export-report Export CSV report
verify-data Verify data integrity
help Show this help
""")
sys.exit(1)
command = sys.argv[1].lower()
utils = LocationAccuracyUtils()
if command == 'check-schema':
utils.check_database_schema()
elif command == 'add-field':
utils.add_location_accuracy_field()
elif command == 'stats':
utils.show_location_accuracy_stats()
elif command == 'export-report':
output_file = sys.argv[2] if len(sys.argv) > 2 else None
utils.export_location_accuracy_report(output_file)
elif command == 'verify-data':
utils.verify_data_integrity()
elif command == 'help':
print(__doc__)
else:
print(f"❌ Unknown command: {command}")
print("Use 'help' to see available commands")
sys.exit(1)
if __name__ == '__main__':
main()