From fc9f4d7f545533bf6f3e7ec123a4e5e77fc10d40 Mon Sep 17 00:00:00 2001 From: Nguyen Ngo Date: Thu, 14 Aug 2025 11:46:48 -0400 Subject: [PATCH] Update export function --- app.py | 66 ++++++++++------ static/js/export_configuration.js | 124 ++++++++++++++++++++---------- 2 files changed, 126 insertions(+), 64 deletions(-) diff --git a/app.py b/app.py index 26b5221..06c9e41 100644 --- a/app.py +++ b/app.py @@ -3601,7 +3601,7 @@ def generate_excel_export(): return redirect(url_for('export_configuration')) def create_excel_export(selected_columns, column_names, filters): - """Create Excel file with selected attendance data""" + """Create Excel file with selected attendance data - Fixed to use QR address (not location)""" try: print(f"📊 Creating Excel export with {len(selected_columns)} columns") @@ -3614,8 +3614,8 @@ def create_excel_export(selected_columns, column_names, filters): print("💡 Install openpyxl: pip install openpyxl") return None - # Build query based on filters - query = db.session.query(AttendanceData).join(QRCode) + # Build query based on filters - JOIN with QRCode to get location_event and location_address + query = db.session.query(AttendanceData, QRCode).join(QRCode, AttendanceData.qr_code_id == QRCode.id) # Apply date filters if filters.get('date_from'): @@ -3644,11 +3644,11 @@ def create_excel_export(selected_columns, column_names, filters): query = query.filter(AttendanceData.employee_id.ilike(f"%{filters['employee_filter']}%")) print(f"📊 Applied employee filter: {filters['employee_filter']}") - # Execute query - records = query.order_by(AttendanceData.check_in_date.desc(), AttendanceData.check_in_time.desc()).all() - print(f"📊 Found {len(records)} records for export") + # Execute query and get results + results = query.order_by(AttendanceData.check_in_date.desc(), AttendanceData.check_in_time.desc()).all() + print(f"📊 Found {len(results)} records for export") - if not records: + if not results: print("⚠️ No records found for export") return None @@ -3671,40 +3671,60 @@ def create_excel_export(selected_columns, column_names, filters): cell.alignment = header_alignment # Add data - for row_idx, record in enumerate(records, 2): + for row_idx, (attendance_record, qr_code) in enumerate(results, 2): for col_idx, column_key in enumerate(selected_columns, 1): cell = ws.cell(row=row_idx, column=col_idx) # Get value based on column key try: if column_key == 'employee_id': - cell.value = record.employee_id + cell.value = attendance_record.employee_id elif column_key == 'location_name': - cell.value = record.location_name + cell.value = attendance_record.location_name elif column_key == 'status': - cell.value = record.status + # Use location_event from QR code instead of status + cell.value = qr_code.location_event if qr_code.location_event else 'Check In' elif column_key == 'check_in_date': - cell.value = record.check_in_date.strftime('%Y-%m-%d') if record.check_in_date else '' + cell.value = attendance_record.check_in_date.strftime('%Y-%m-%d') if attendance_record.check_in_date else '' elif column_key == 'check_in_time': - cell.value = record.check_in_time.strftime('%H:%M:%S') if record.check_in_time else '' + cell.value = attendance_record.check_in_time.strftime('%H:%M:%S') if attendance_record.check_in_time else '' elif column_key == 'qr_address': - cell.value = record.qr_code.location if record.qr_code else '' + # FIXED: Use QR Code address (location_address), not location + cell.value = qr_code.location_address if qr_code and qr_code.location_address else '' elif column_key == 'address': - cell.value = record.address or '' + # IMPLEMENTED: Check-in address logic based on location accuracy + # If location accuracy <= 0.5 miles, use QR address; otherwise use actual check-in address + if hasattr(attendance_record, 'location_accuracy') and attendance_record.location_accuracy is not None: + try: + accuracy_value = float(attendance_record.location_accuracy) + if accuracy_value <= 0.5: + # High accuracy - use QR code ADDRESS (not location) + cell.value = qr_code.location_address if qr_code and qr_code.location_address else '' + print(f"📍 Using QR address for employee {attendance_record.employee_id} (accuracy: {accuracy_value:.3f} miles)") + else: + # Lower accuracy - use actual check-in address + cell.value = attendance_record.address or '' + print(f"📍 Using check-in address for employee {attendance_record.employee_id} (accuracy: {accuracy_value:.3f} miles)") + except (ValueError, TypeError): + # If accuracy can't be converted to float, use check-in address + cell.value = attendance_record.address or '' + else: + # No location accuracy data - use actual check-in address + cell.value = attendance_record.address or '' elif column_key == 'device_info': - cell.value = record.device_info or '' + cell.value = attendance_record.device_info or '' elif column_key == 'ip_address': - cell.value = record.ip_address or '' + cell.value = attendance_record.ip_address or '' elif column_key == 'user_agent': - cell.value = record.user_agent or '' + cell.value = attendance_record.user_agent or '' elif column_key == 'latitude': - cell.value = record.latitude or '' + cell.value = attendance_record.latitude or '' elif column_key == 'longitude': - cell.value = record.longitude or '' + cell.value = attendance_record.longitude or '' elif column_key == 'accuracy': - cell.value = record.accuracy or '' + cell.value = attendance_record.accuracy or '' elif column_key == 'location_accuracy': - cell.value = record.location_accuracy or '' + cell.value = attendance_record.location_accuracy or '' else: cell.value = '' except Exception as cell_error: @@ -3729,7 +3749,7 @@ def create_excel_export(selected_columns, column_names, filters): wb.save(excel_buffer) excel_buffer.seek(0) - print("📊 Excel file created successfully") + print("📊 Excel file created successfully with QR address (not QR location)") return excel_buffer except Exception as e: diff --git a/static/js/export_configuration.js b/static/js/export_configuration.js index ce8fa20..5c552ba 100644 --- a/static/js/export_configuration.js +++ b/static/js/export_configuration.js @@ -53,53 +53,94 @@ function toggleColumnName(columnKey) { } function updatePreview() { - const previewHeader = document.getElementById('previewHeader'); - const previewTable = document.querySelector('.preview-table tbody'); - - if (!previewHeader || !previewTable) return; - - const selectedColumns = Array.from(document.querySelectorAll('input[name="selected_columns"]:checked')); - - if (selectedColumns.length === 0) { - previewHeader.innerHTML = ''; - previewTable.innerHTML = 'No columns selected'; - updateGenerateButton(0); - return; + try { + const previewHeader = document.getElementById('previewHeader'); + const previewTable = document.querySelector('.preview-table tbody'); + + if (!previewHeader || !previewTable) { + console.warn('Preview elements not found'); + return; + } + + // Get selected columns + const selectedColumns = Array.from(document.querySelectorAll('input[name="selected_columns"]:checked')); + + if (selectedColumns.length === 0) { + // No columns selected + previewHeader.innerHTML = ''; + previewTable.innerHTML = ` + + + + No columns selected - please select at least one column to export + + + `; + updateGenerateButton(0); + return; + } + + // Build header + let headerHTML = ''; + selectedColumns.forEach(checkbox => { + const columnKey = checkbox.value; + const nameInput = document.getElementById('name_' + columnKey); + const customName = nameInput ? nameInput.value : columnKey; + + headerHTML += `${customName}`; + }); + previewHeader.innerHTML = headerHTML; + + // Build sample data row with enhanced address logic preview + let sampleRowHTML = ''; + selectedColumns.forEach(checkbox => { + const columnKey = checkbox.value; + let sampleData = getSampleData(columnKey); + + // Special handling for address column to show the logic + if (columnKey === 'address') { + sampleData = `123 Business St, City*`; + } + + sampleRowHTML += `${sampleData}`; + }); + sampleRowHTML += ''; + + // Add explanation row if address column is selected + const hasAddressColumn = selectedColumns.some(cb => cb.value === 'address'); + if (hasAddressColumn) { + sampleRowHTML += ` + + + + * Check-in Address: Shows QR address when location accuracy ≤ 0.5 miles, otherwise shows actual GPS address + + + `; + } + + previewTable.innerHTML = sampleRowHTML; + + // Update generate button + updateGenerateButton(selectedColumns.length); + + console.log(`Preview updated with ${selectedColumns.length} columns`); + } catch (error) { + console.error('Error updating preview:', error); } - - // Build header - let headerHTML = ''; - selectedColumns.forEach(function(checkbox) { - const columnKey = checkbox.value; - const nameInput = document.getElementById('name_' + columnKey); - const customName = nameInput ? nameInput.value : columnKey; - headerHTML += '' + customName + ''; - }); - previewHeader.innerHTML = headerHTML; - - // Build sample row - let sampleRowHTML = ''; - selectedColumns.forEach(function(checkbox) { - const columnKey = checkbox.value; - const sampleData = getSampleData(columnKey); - sampleRowHTML += '' + sampleData + ''; - }); - sampleRowHTML += ''; - previewTable.innerHTML = sampleRowHTML; - - updateGenerateButton(selectedColumns.length); } function getSampleData(columnKey) { - const samples = { + // Return sample data for each column type - Updated with correct event types + const sampleData = { 'employee_id': 'EMP001', 'location_name': 'Main Office', - 'status': 'Check In', - 'check_in_date': '2025-01-15', + 'status': 'Check In', // This will show "Check In" or "Check Out" from QR code + 'check_in_date': '2025-08-14', 'check_in_time': '09:30:00', - 'qr_address': '123 Business St', - 'address': '123 Business St', - 'device_info': 'iPhone', + 'qr_address': '123 Business St, City', + 'address': '123 Business St, City', + 'device_info': 'iPhone 14 Pro', 'ip_address': '192.168.1.100', 'user_agent': 'Mobile Safari', 'latitude': '40.7128', @@ -107,7 +148,8 @@ function getSampleData(columnKey) { 'accuracy': '5.2', 'location_accuracy': '0.003' }; - return samples[columnKey] || 'Sample'; + + return sampleData[columnKey] || 'Sample Data'; } function updateGenerateButton(columnCount) {