Updated attendance report page

This commit is contained in:
2025-08-02 12:56:46 -04:00
parent 5cb8bc8b89
commit fc8326be92
4 changed files with 1113 additions and 734 deletions
+92 -10
View File
@@ -1617,18 +1617,86 @@ def toggle_qr_status_api(qr_id):
@app.route('/attendance')
@admin_required
def attendance_report():
"""Attendance report page (Admin only)"""
"""Enhanced attendance report page with date range filtering and location data (Admin only)"""
try:
# Get filter parameters
date_filter = request.args.get('date', '')
date_from = request.args.get('date_from', '')
date_to = request.args.get('date_to', '')
location_filter = request.args.get('location', '')
employee_filter = request.args.get('employee', '')
# Base query using the view
query = db.session.execute(text("SELECT * FROM attendance_report WHERE 1=1"))
# Build base query with enhanced location data
base_query = """
SELECT
ad.id,
ad.employee_id,
ad.check_in_date,
ad.check_in_time,
ad.location_name,
qc.location_event,
qc.location_address as qr_address,
ad.address as checked_in_address,
ad.latitude,
ad.longitude,
ad.accuracy,
ad.device_info
FROM attendance_data ad
LEFT JOIN qr_codes qc ON ad.qr_code_id = qc.id
WHERE 1=1
"""
# Apply filters (you can enhance this with proper SQLAlchemy filtering)
attendance_records = query.fetchall()
conditions = []
params = {}
# Apply date range filter
if date_from:
conditions.append("ad.check_in_date >= :date_from")
params['date_from'] = date_from
if date_to:
conditions.append("ad.check_in_date <= :date_to")
params['date_to'] = date_to
# Apply location filter
if location_filter:
conditions.append("ad.location_name ILIKE :location")
params['location'] = f"%{location_filter}%"
# Apply employee filter
if employee_filter:
conditions.append("ad.employee_id ILIKE :employee")
params['employee'] = f"%{employee_filter}%"
# Add conditions to query
if conditions:
base_query += " AND " + " AND ".join(conditions)
# Add ordering
base_query += " ORDER BY ad.check_in_date DESC, ad.check_in_time DESC"
# Execute query
query_result = db.session.execute(text(base_query), params)
attendance_records = query_result.fetchall()
# Process records to add calculated fields
processed_records = []
for record in attendance_records:
record_dict = {
'id': record.id,
'employee_id': record.employee_id,
'check_in_date': record.check_in_date,
'check_in_time': record.check_in_time,
'location_name': record.location_name,
'location_event': record.location_event,
'qr_address': record.qr_address or 'Not available',
'checked_in_address': record.checked_in_address or 'Location not captured',
'device_info': record.device_info,
'accuracy': record.accuracy,
'accuracy_level': get_accuracy_level(record.accuracy),
'has_location_data': record.latitude is not None and record.longitude is not None,
'coordinates': f"{record.latitude:.6f}, {record.longitude:.6f}" if record.latitude and record.longitude else "No GPS data"
}
processed_records.append(record_dict)
# Get unique locations for filter dropdown
locations_query = db.session.execute(text("""
@@ -1644,7 +1712,9 @@ def attendance_report():
COUNT(*) as total_checkins,
COUNT(DISTINCT employee_id) as unique_employees,
COUNT(DISTINCT qr_code_id) as active_locations,
COUNT(CASE WHEN check_in_date = CURRENT_DATE THEN 1 END) as today_checkins
COUNT(CASE WHEN check_in_date = CURRENT_DATE THEN 1 END) as today_checkins,
COUNT(CASE WHEN latitude IS NOT NULL AND longitude IS NOT NULL THEN 1 END) as records_with_gps,
AVG(accuracy) as avg_accuracy
FROM attendance_data
"""))
stats = stats_query.fetchone()
@@ -1654,10 +1724,11 @@ def attendance_report():
current_date_formatted = datetime.now().strftime('%B %d')
return render_template('attendance_report.html',
attendance_records=attendance_records,
attendance_records=processed_records,
locations=locations,
stats=stats,
date_filter=date_filter,
date_from=date_from,
date_to=date_to,
location_filter=location_filter,
employee_filter=employee_filter,
today_date=today_date,
@@ -1665,8 +1736,19 @@ def attendance_report():
except Exception as e:
print(f"Error loading attendance report: {e}")
flash('Error loading attendance report.', 'error')
flash('Error loading attendance report. Please try again.', 'error')
return redirect(url_for('dashboard'))
def get_accuracy_level(accuracy):
"""Get human-readable accuracy level"""
if not accuracy:
return 'unknown'
elif accuracy <= 50:
return 'high'
elif accuracy <= 100:
return 'medium'
else:
return 'low'
@app.route('/api/attendance/stats')
@admin_required