Fix attendance report filter button issues

This commit is contained in:
2025-08-14 22:05:06 -04:00
parent 7341a23d39
commit 455105f832
3 changed files with 314 additions and 215 deletions
+85 -74
View File
@@ -1233,7 +1233,7 @@ def login():
try:
# Find user (case-insensitive username)
user = User.query.filter(
User.username.ilike(username),
User.username.like(username),
User.active_status == True
).first()
@@ -2405,7 +2405,7 @@ def create_qr_code():
'create',
additional_info={'location_event': location_event}
)
# Success message with coordinates info
project_info = f" in project '{project.name}'" if project else ""
coord_info = f" with coordinates ({new_qr_code.coordinates_display})" if has_coordinates else ""
@@ -2997,6 +2997,12 @@ def attendance_report():
try:
print("📊 Loading attendance report...")
# Log attendance report access
try:
logger_handler.logger.info(f"User {session.get('username', 'unknown')} accessed attendance report")
except Exception as log_error:
print(f"⚠️ Logging error (non-critical): {log_error}")
# Check if location_accuracy column exists
has_location_accuracy = check_location_accuracy_column_exists()
print(f"🔍 Location accuracy column exists: {has_location_accuracy}")
@@ -3066,17 +3072,18 @@ def attendance_report():
# Apply location filter
if location_filter:
conditions.append("ad.location_name ILIKE :location")
conditions.append("ad.location_name LIKE :location")
params['location'] = f"%{location_filter}%"
# Apply employee filter
if employee_filter:
conditions.append("ad.employee_id ILIKE :employee")
conditions.append("ad.employee_id LIKE :employee")
params['employee'] = f"%{employee_filter}%"
# FIXED: Apply project filter using SQL approach (not ORM)
if project_filter:
# Join with QRCode and filter by project_id
query = query.join(QRCode, AttendanceData.qr_code_id == QRCode.id).filter(QRCode.project_id == int(project_filter))
conditions.append("qc.project_id = :project_id")
params['project_id'] = int(project_filter)
print(f"📊 Applied project filter: {project_filter}")
# Add conditions to query
@@ -3100,80 +3107,78 @@ def attendance_report():
# Safe attribute access with fallbacks
location_accuracy = getattr(record, 'location_accuracy', None)
gps_accuracy = getattr(record, 'gps_accuracy', None)
qr_address = getattr(record, 'qr_address', None)
# Handle location accuracy for address display logic
if location_accuracy is not None and location_accuracy != "None":
try:
accuracy_value = float(location_accuracy) if isinstance(location_accuracy, str) else location_accuracy
checked_in_address = qr_address if (accuracy_value <= 0.5) else getattr(record, 'checked_in_address', None)
except (ValueError, TypeError):
checked_in_address = getattr(record, 'checked_in_address', None)
else:
checked_in_address = getattr(record, 'checked_in_address', None)
# CRITICAL FIX: Properly handle check_in_time formatting
check_in_time_value = record.check_in_time
# Handle different possible types for check_in_time
if isinstance(check_in_time_value, timedelta):
# Convert timedelta to time object
total_seconds = int(check_in_time_value.total_seconds())
hours = total_seconds // 3600
minutes = (total_seconds % 3600) // 60
seconds = total_seconds % 60
formatted_time = time(hours % 24, minutes, seconds)
print(f"⚠️ Converted timedelta to time: {check_in_time_value} -> {formatted_time}")
elif isinstance(check_in_time_value, time):
# Already a time object, use as-is
formatted_time = check_in_time_value
elif isinstance(check_in_time_value, datetime):
# Extract time component from datetime
formatted_time = check_in_time_value.time()
elif isinstance(check_in_time_value, str):
# Try to parse string to time
try:
formatted_time = datetime.strptime(check_in_time_value, '%H:%M:%S').time()
except ValueError:
try:
formatted_time = datetime.strptime(check_in_time_value, '%H:%M').time()
except ValueError:
# Fallback to current time if parsing fails
formatted_time = datetime.now().time()
print(f"⚠️ Could not parse time string: {check_in_time_value}, using current time")
else:
# Fallback to current time for any other type
formatted_time = datetime.now().time()
print(f"⚠️ Unexpected check_in_time type: {type(check_in_time_value)}, using current time")
# Create the record dictionary with properly formatted time
record_dict = {
# Create processed record with calculated fields
processed_record = {
'id': record.id,
'employee_id': record.employee_id,
'employee_id': record.employee_id or 'Unknown',
'check_in_date': record.check_in_date,
'check_in_time': formatted_time, # Now guaranteed to be a time object
'location_name': record.location_name,
'location_event': getattr(record, 'location_event', ''),
'qr_address': qr_address or 'Not available',
'checked_in_address': checked_in_address or 'Location not captured',
'device_info': getattr(record, 'device_info', ''),
'check_in_time': record.check_in_time,
'location_name': record.location_name or 'Unknown Location',
'location_event': getattr(record, 'location_event', None) or 'Check In',
'qr_address': getattr(record, 'qr_address', None) or 'N/A',
'checked_in_address': getattr(record, 'checked_in_address', None) or 'N/A',
'latitude': record.latitude,
'longitude': record.longitude,
'location_accuracy': location_accuracy,
'gps_accuracy': gps_accuracy,
'accuracy_level': get_location_accuracy_level(location_accuracy) if location_accuracy else 'unknown',
'has_location_data': record.latitude is not None and record.longitude is not None,
'coordinates': f"{record.latitude:.10f}, {record.longitude:.10f}" if record.latitude and record.longitude else "No GPS data",
'has_location_accuracy_feature': has_location_accuracy
'device_info': getattr(record, 'device_info', None) or 'Unknown Device',
}
processed_records.append(record_dict)
print(f"✅ Processed {len(processed_records)} records")
# FIXED: Add address display logic based on location accuracy
# If location accuracy <= 0.5 miles, display QR address; otherwise display actual check-in address
if location_accuracy is not None:
try:
accuracy_value = float(location_accuracy) if isinstance(location_accuracy, str) else location_accuracy
if accuracy_value <= 0.5:
# High accuracy - use QR code address
processed_record['display_address'] = getattr(record, 'qr_address', None) or 'N/A'
processed_record['address_source'] = 'qr'
print(f"📍 Using QR address for employee {record.employee_id} (accuracy: {accuracy_value:.3f} miles)")
else:
# Lower accuracy - use actual check-in address
processed_record['display_address'] = getattr(record, 'checked_in_address', None) or 'N/A'
processed_record['address_source'] = 'checkin'
print(f"📍 Using check-in address for employee {record.employee_id} (accuracy: {accuracy_value:.3f} miles)")
except (ValueError, TypeError):
# If accuracy can't be converted to float, use check-in address
processed_record['display_address'] = getattr(record, 'checked_in_address', None) or 'N/A'
processed_record['address_source'] = 'checkin'
else:
# No location accuracy data - use actual check-in address
processed_record['display_address'] = getattr(record, 'checked_in_address', None) or 'N/A'
processed_record['address_source'] = 'checkin'
# Add accuracy level calculation if location_accuracy exists
if location_accuracy is not None:
if location_accuracy <= 10:
processed_record['accuracy_level'] = 'High'
elif location_accuracy <= 50:
processed_record['accuracy_level'] = 'Medium'
else:
processed_record['accuracy_level'] = 'Low'
else:
processed_record['accuracy_level'] = 'Unknown'
# Add formatted datetime for display
try:
if record.check_in_date and record.check_in_time:
datetime_obj = datetime.combine(record.check_in_date, record.check_in_time)
processed_record['formatted_datetime'] = datetime_obj.strftime('%m/%d/%Y %I:%M %p')
else:
processed_record['formatted_datetime'] = 'Invalid Date/Time'
except Exception as e:
print(f"⚠️ Error formatting datetime for record {record.id}: {e}")
processed_record['formatted_datetime'] = 'Error'
processed_records.append(processed_record)
# Get unique locations for filter dropdown
try:
locations_query = db.session.execute(text("""
SELECT DISTINCT location_name
FROM attendance_data
WHERE location_name IS NOT NULL
WHERE location_name IS NOT NULL
ORDER BY location_name
"""))
locations = [row[0] for row in locations_query.fetchall()]
@@ -3182,7 +3187,7 @@ def attendance_report():
print(f"⚠️ Error loading locations: {e}")
locations = []
# Update the locations query to get only active projects for the dropdown
# Update the projects query to get only active projects for the dropdown
try:
projects = db.session.execute(text("""
SELECT p.id, p.name, COUNT(DISTINCT ad.id) as attendance_count
@@ -3242,7 +3247,7 @@ def attendance_report():
})()
# Add today's date for template
today_date = datetime.now().strftime('%m/%d/%Y')
today_date = datetime.now().strftime('%Y-%m-%d')
current_date_formatted = datetime.now().strftime('%B %d')
print("✅ Rendering attendance report template")
@@ -3267,6 +3272,12 @@ def attendance_report():
import traceback
print(f"❌ Traceback: {traceback.format_exc()}")
# Log the error
try:
logger_handler.log_database_error('attendance_report', e)
except Exception as log_error:
print(f"⚠️ Additional logging error: {log_error}")
flash('Error loading attendance report. Please check the server logs for details.', 'error')
return redirect(url_for('dashboard'))
@@ -3693,12 +3704,12 @@ def create_excel_export(selected_columns, column_names, filters):
# Apply location filter
if filters.get('location_filter'):
query = query.filter(AttendanceData.location_name.ilike(f"%{filters['location_filter']}%"))
query = query.filter(AttendanceData.location_name.like(f"%{filters['location_filter']}%"))
print(f"📊 Applied location filter: {filters['location_filter']}")
# Apply employee filter
if filters.get('employee_filter'):
query = query.filter(AttendanceData.employee_id.ilike(f"%{filters['employee_filter']}%"))
query = query.filter(AttendanceData.employee_id.like(f"%{filters['employee_filter']}%"))
print(f"📊 Applied employee filter: {filters['employee_filter']}")
# Apply project filter
@@ -3856,12 +3867,12 @@ def create_excel_export_ordered(selected_columns, column_names, filters):
# Apply location filter
if filters.get('location_filter'):
query = query.filter(AttendanceData.location_name.ilike(f"%{filters['location_filter']}%"))
query = query.filter(AttendanceData.location_name.like(f"%{filters['location_filter']}%"))
print(f"📊 Applied location filter: {filters['location_filter']}")
# Apply employee filter
if filters.get('employee_filter'):
query = query.filter(AttendanceData.employee_id.ilike(f"%{filters['employee_filter']}%"))
query = query.filter(AttendanceData.employee_id.like(f"%{filters['employee_filter']}%"))
print(f"📊 Applied employee filter: {filters['employee_filter']}")
# Execute query and get results