Update export function
This commit is contained in:
@@ -3601,7 +3601,7 @@ def generate_excel_export():
|
|||||||
return redirect(url_for('export_configuration'))
|
return redirect(url_for('export_configuration'))
|
||||||
|
|
||||||
def create_excel_export(selected_columns, column_names, filters):
|
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:
|
try:
|
||||||
print(f"📊 Creating Excel export with {len(selected_columns)} columns")
|
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")
|
print("💡 Install openpyxl: pip install openpyxl")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Build query based on filters
|
# Build query based on filters - JOIN with QRCode to get location_event and location_address
|
||||||
query = db.session.query(AttendanceData).join(QRCode)
|
query = db.session.query(AttendanceData, QRCode).join(QRCode, AttendanceData.qr_code_id == QRCode.id)
|
||||||
|
|
||||||
# Apply date filters
|
# Apply date filters
|
||||||
if filters.get('date_from'):
|
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']}%"))
|
query = query.filter(AttendanceData.employee_id.ilike(f"%{filters['employee_filter']}%"))
|
||||||
print(f"📊 Applied employee filter: {filters['employee_filter']}")
|
print(f"📊 Applied employee filter: {filters['employee_filter']}")
|
||||||
|
|
||||||
# Execute query
|
# Execute query and get results
|
||||||
records = query.order_by(AttendanceData.check_in_date.desc(), AttendanceData.check_in_time.desc()).all()
|
results = query.order_by(AttendanceData.check_in_date.desc(), AttendanceData.check_in_time.desc()).all()
|
||||||
print(f"📊 Found {len(records)} records for export")
|
print(f"📊 Found {len(results)} records for export")
|
||||||
|
|
||||||
if not records:
|
if not results:
|
||||||
print("⚠️ No records found for export")
|
print("⚠️ No records found for export")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -3671,40 +3671,60 @@ def create_excel_export(selected_columns, column_names, filters):
|
|||||||
cell.alignment = header_alignment
|
cell.alignment = header_alignment
|
||||||
|
|
||||||
# Add data
|
# 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):
|
for col_idx, column_key in enumerate(selected_columns, 1):
|
||||||
cell = ws.cell(row=row_idx, column=col_idx)
|
cell = ws.cell(row=row_idx, column=col_idx)
|
||||||
|
|
||||||
# Get value based on column key
|
# Get value based on column key
|
||||||
try:
|
try:
|
||||||
if column_key == 'employee_id':
|
if column_key == 'employee_id':
|
||||||
cell.value = record.employee_id
|
cell.value = attendance_record.employee_id
|
||||||
elif column_key == 'location_name':
|
elif column_key == 'location_name':
|
||||||
cell.value = record.location_name
|
cell.value = attendance_record.location_name
|
||||||
elif column_key == 'status':
|
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':
|
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':
|
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':
|
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':
|
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':
|
elif column_key == 'device_info':
|
||||||
cell.value = record.device_info or ''
|
cell.value = attendance_record.device_info or ''
|
||||||
elif column_key == 'ip_address':
|
elif column_key == 'ip_address':
|
||||||
cell.value = record.ip_address or ''
|
cell.value = attendance_record.ip_address or ''
|
||||||
elif column_key == 'user_agent':
|
elif column_key == 'user_agent':
|
||||||
cell.value = record.user_agent or ''
|
cell.value = attendance_record.user_agent or ''
|
||||||
elif column_key == 'latitude':
|
elif column_key == 'latitude':
|
||||||
cell.value = record.latitude or ''
|
cell.value = attendance_record.latitude or ''
|
||||||
elif column_key == 'longitude':
|
elif column_key == 'longitude':
|
||||||
cell.value = record.longitude or ''
|
cell.value = attendance_record.longitude or ''
|
||||||
elif column_key == 'accuracy':
|
elif column_key == 'accuracy':
|
||||||
cell.value = record.accuracy or ''
|
cell.value = attendance_record.accuracy or ''
|
||||||
elif column_key == 'location_accuracy':
|
elif column_key == 'location_accuracy':
|
||||||
cell.value = record.location_accuracy or ''
|
cell.value = attendance_record.location_accuracy or ''
|
||||||
else:
|
else:
|
||||||
cell.value = ''
|
cell.value = ''
|
||||||
except Exception as cell_error:
|
except Exception as cell_error:
|
||||||
@@ -3729,7 +3749,7 @@ def create_excel_export(selected_columns, column_names, filters):
|
|||||||
wb.save(excel_buffer)
|
wb.save(excel_buffer)
|
||||||
excel_buffer.seek(0)
|
excel_buffer.seek(0)
|
||||||
|
|
||||||
print("📊 Excel file created successfully")
|
print("📊 Excel file created successfully with QR address (not QR location)")
|
||||||
return excel_buffer
|
return excel_buffer
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -53,53 +53,94 @@ function toggleColumnName(columnKey) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updatePreview() {
|
function updatePreview() {
|
||||||
const previewHeader = document.getElementById('previewHeader');
|
try {
|
||||||
const previewTable = document.querySelector('.preview-table tbody');
|
const previewHeader = document.getElementById('previewHeader');
|
||||||
|
const previewTable = document.querySelector('.preview-table tbody');
|
||||||
|
|
||||||
if (!previewHeader || !previewTable) return;
|
if (!previewHeader || !previewTable) {
|
||||||
|
console.warn('Preview elements not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const selectedColumns = Array.from(document.querySelectorAll('input[name="selected_columns"]:checked'));
|
// Get selected columns
|
||||||
|
const selectedColumns = Array.from(document.querySelectorAll('input[name="selected_columns"]:checked'));
|
||||||
|
|
||||||
if (selectedColumns.length === 0) {
|
if (selectedColumns.length === 0) {
|
||||||
previewHeader.innerHTML = '';
|
// No columns selected
|
||||||
previewTable.innerHTML = '<tr><td colspan="100%" style="text-align:center; padding:2rem;">No columns selected</td></tr>';
|
previewHeader.innerHTML = '';
|
||||||
updateGenerateButton(0);
|
previewTable.innerHTML = `
|
||||||
return;
|
<tr>
|
||||||
|
<td colspan="100%" class="preview-placeholder">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
No columns selected - please select at least one column to export
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
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 += `<th>${customName}</th>`;
|
||||||
|
});
|
||||||
|
previewHeader.innerHTML = headerHTML;
|
||||||
|
|
||||||
|
// Build sample data row with enhanced address logic preview
|
||||||
|
let sampleRowHTML = '<tr>';
|
||||||
|
selectedColumns.forEach(checkbox => {
|
||||||
|
const columnKey = checkbox.value;
|
||||||
|
let sampleData = getSampleData(columnKey);
|
||||||
|
|
||||||
|
// Special handling for address column to show the logic
|
||||||
|
if (columnKey === 'address') {
|
||||||
|
sampleData = `<span title="If location accuracy ≤ 0.5 miles: shows QR address, otherwise: shows actual check-in address">123 Business St, City*</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleRowHTML += `<td>${sampleData}</td>`;
|
||||||
|
});
|
||||||
|
sampleRowHTML += '</tr>';
|
||||||
|
|
||||||
|
// Add explanation row if address column is selected
|
||||||
|
const hasAddressColumn = selectedColumns.some(cb => cb.value === 'address');
|
||||||
|
if (hasAddressColumn) {
|
||||||
|
sampleRowHTML += `
|
||||||
|
<tr style="background-color: #f8f9fa; font-size: 0.85em; color: #6c757d;">
|
||||||
|
<td colspan="${selectedColumns.length}" style="text-align: center; padding: 0.75rem; font-style: italic;">
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
* Check-in Address: Shows QR address when location accuracy ≤ 0.5 miles, otherwise shows actual GPS address
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 += '<th>' + customName + '</th>';
|
|
||||||
});
|
|
||||||
previewHeader.innerHTML = headerHTML;
|
|
||||||
|
|
||||||
// Build sample row
|
|
||||||
let sampleRowHTML = '<tr>';
|
|
||||||
selectedColumns.forEach(function(checkbox) {
|
|
||||||
const columnKey = checkbox.value;
|
|
||||||
const sampleData = getSampleData(columnKey);
|
|
||||||
sampleRowHTML += '<td>' + sampleData + '</td>';
|
|
||||||
});
|
|
||||||
sampleRowHTML += '</tr>';
|
|
||||||
previewTable.innerHTML = sampleRowHTML;
|
|
||||||
|
|
||||||
updateGenerateButton(selectedColumns.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSampleData(columnKey) {
|
function getSampleData(columnKey) {
|
||||||
const samples = {
|
// Return sample data for each column type - Updated with correct event types
|
||||||
|
const sampleData = {
|
||||||
'employee_id': 'EMP001',
|
'employee_id': 'EMP001',
|
||||||
'location_name': 'Main Office',
|
'location_name': 'Main Office',
|
||||||
'status': 'Check In',
|
'status': 'Check In', // This will show "Check In" or "Check Out" from QR code
|
||||||
'check_in_date': '2025-01-15',
|
'check_in_date': '2025-08-14',
|
||||||
'check_in_time': '09:30:00',
|
'check_in_time': '09:30:00',
|
||||||
'qr_address': '123 Business St',
|
'qr_address': '123 Business St, City',
|
||||||
'address': '123 Business St',
|
'address': '123 Business St, City',
|
||||||
'device_info': 'iPhone',
|
'device_info': 'iPhone 14 Pro',
|
||||||
'ip_address': '192.168.1.100',
|
'ip_address': '192.168.1.100',
|
||||||
'user_agent': 'Mobile Safari',
|
'user_agent': 'Mobile Safari',
|
||||||
'latitude': '40.7128',
|
'latitude': '40.7128',
|
||||||
@@ -107,7 +148,8 @@ function getSampleData(columnKey) {
|
|||||||
'accuracy': '5.2',
|
'accuracy': '5.2',
|
||||||
'location_accuracy': '0.003'
|
'location_accuracy': '0.003'
|
||||||
};
|
};
|
||||||
return samples[columnKey] || 'Sample';
|
|
||||||
|
return sampleData[columnKey] || 'Sample Data';
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateGenerateButton(columnCount) {
|
function updateGenerateButton(columnCount) {
|
||||||
|
|||||||
Reference in New Issue
Block a user