Modifed Time Attendance report exported file name
This commit is contained in:
@@ -7204,23 +7204,48 @@ def export_time_attendance():
|
|||||||
|
|
||||||
# Generate filename
|
# Generate filename
|
||||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||||
|
|
||||||
|
# Format dates for filename (MMDDYYYY format)
|
||||||
|
date_from_formatted = ''
|
||||||
|
date_to_formatted = ''
|
||||||
|
if start_date:
|
||||||
|
try:
|
||||||
|
date_obj = datetime.strptime(start_date, '%Y-%m-%d')
|
||||||
|
date_from_formatted = date_obj.strftime('%m%d%Y')
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if end_date:
|
||||||
|
try:
|
||||||
|
date_obj = datetime.strptime(end_date, '%Y-%m-%d')
|
||||||
|
date_to_formatted = date_obj.strftime('%m%d%Y')
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Build filename with date range
|
||||||
|
# Format: time_attendance_[fromdate_todate].xlsx/csv
|
||||||
|
date_range_str = ''
|
||||||
|
if date_from_formatted and date_to_formatted:
|
||||||
|
date_range_str = f"{date_from_formatted}_{date_to_formatted}"
|
||||||
|
elif date_from_formatted:
|
||||||
|
date_range_str = f"{date_from_formatted}"
|
||||||
|
elif date_to_formatted:
|
||||||
|
date_range_str = f"{date_to_formatted}"
|
||||||
|
|
||||||
|
# Keep the filter_str for backward compatibility (but not in filename anymore)
|
||||||
filter_desc = []
|
filter_desc = []
|
||||||
if employee_filter:
|
if employee_filter:
|
||||||
filter_desc.append(f"emp_{employee_filter}")
|
filter_desc.append(f"emp_{employee_filter}")
|
||||||
if location_filter:
|
if location_filter:
|
||||||
filter_desc.append(f"loc_{location_filter[:10]}")
|
filter_desc.append(f"loc_{location_filter[:10]}")
|
||||||
if start_date:
|
|
||||||
filter_desc.append(f"from_{start_date}")
|
|
||||||
if end_date:
|
|
||||||
filter_desc.append(f"to_{end_date}")
|
|
||||||
|
|
||||||
filter_str = "_".join(filter_desc) if filter_desc else "all"
|
filter_str = "_".join(filter_desc) if filter_desc else "all"
|
||||||
|
|
||||||
# Export based on format
|
# Export based on format
|
||||||
if export_format == 'excel' or export_format == 'xlsx':
|
if export_format == 'excel' or export_format == 'xlsx':
|
||||||
return export_time_attendance_excel(records, timestamp, filter_str)
|
return export_time_attendance_excel(records, timestamp, date_range_str, filter_str)
|
||||||
else:
|
else:
|
||||||
return export_time_attendance_csv(records, timestamp, filter_str)
|
return export_time_attendance_csv(records, timestamp, date_range_str, filter_str)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger_handler.logger.error(f"Error exporting time attendance records: {e}")
|
logger_handler.logger.error(f"Error exporting time attendance records: {e}")
|
||||||
@@ -7228,7 +7253,7 @@ def export_time_attendance():
|
|||||||
return redirect(url_for('time_attendance_records'))
|
return redirect(url_for('time_attendance_records'))
|
||||||
|
|
||||||
|
|
||||||
def export_time_attendance_csv(records, timestamp, filter_str):
|
def export_time_attendance_csv(records, timestamp, date_range_str, filter_str):
|
||||||
"""Generate CSV export of time attendance records"""
|
"""Generate CSV export of time attendance records"""
|
||||||
import csv
|
import csv
|
||||||
import io
|
import io
|
||||||
@@ -7264,9 +7289,9 @@ def export_time_attendance_csv(records, timestamp, filter_str):
|
|||||||
record.recorded_address or ''
|
record.recorded_address or ''
|
||||||
])
|
])
|
||||||
|
|
||||||
# Prepare response
|
# Prepare response with new filename format
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
filename = f'time_attendance_{filter_str}_{timestamp}.csv'
|
filename = f'time_attendance_{date_range_str}.csv'
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
output.getvalue(),
|
output.getvalue(),
|
||||||
@@ -7276,7 +7301,7 @@ def export_time_attendance_csv(records, timestamp, filter_str):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def export_time_attendance_excel(records, timestamp, filter_str):
|
def export_time_attendance_excel(records, timestamp, date_range_str, filter_str):
|
||||||
"""Generate Excel export of time attendance records"""
|
"""Generate Excel export of time attendance records"""
|
||||||
import io
|
import io
|
||||||
from openpyxl import Workbook
|
from openpyxl import Workbook
|
||||||
@@ -7298,126 +7323,49 @@ def export_time_attendance_excel(records, timestamp, filter_str):
|
|||||||
bottom=Side(style='thin')
|
bottom=Side(style='thin')
|
||||||
)
|
)
|
||||||
|
|
||||||
# Headers
|
# Header row
|
||||||
headers = [
|
headers = ['Employee ID', 'Employee Name', 'Platform', 'Date', 'Time',
|
||||||
'Employee ID', 'Employee Name', 'Platform', 'Date', 'Time',
|
'Location Name', 'Action Description', 'Event Description', 'Recorded Address']
|
||||||
'Location Name', 'Action Description', 'Event Description',
|
|
||||||
'Recorded Address'
|
|
||||||
]
|
|
||||||
|
|
||||||
# Write headers
|
|
||||||
for col_num, header in enumerate(headers, 1):
|
for col_num, header in enumerate(headers, 1):
|
||||||
cell = ws.cell(row=1, column=col_num)
|
cell = ws.cell(row=1, column=col_num, value=header)
|
||||||
cell.value = header
|
|
||||||
cell.fill = header_fill
|
cell.fill = header_fill
|
||||||
cell.font = header_font
|
cell.font = header_font
|
||||||
cell.alignment = Alignment(horizontal='center', vertical='center')
|
cell.alignment = Alignment(horizontal='center', vertical='center')
|
||||||
cell.border = border
|
cell.border = border
|
||||||
|
|
||||||
# Write data
|
# Data rows
|
||||||
for row_num, record in enumerate(records, 2):
|
for row_num, record in enumerate(records, 2):
|
||||||
data = [
|
ws.cell(row=row_num, column=1, value=record.employee_id).border = border
|
||||||
record.employee_id,
|
ws.cell(row=row_num, column=2, value=record.employee_name).border = border
|
||||||
record.employee_name,
|
ws.cell(row=row_num, column=3, value=record.platform or '').border = border
|
||||||
record.platform or '',
|
ws.cell(row=row_num, column=4, value=record.attendance_date.strftime('%Y-%m-%d') if record.attendance_date else '').border = border
|
||||||
record.attendance_date.strftime('%Y-%m-%d') if record.attendance_date else '',
|
ws.cell(row=row_num, column=5, value=record.attendance_time.strftime('%H:%M:%S') if record.attendance_time else '').border = border
|
||||||
record.attendance_time.strftime('%H:%M:%S') if record.attendance_time else '',
|
ws.cell(row=row_num, column=6, value=record.location_name).border = border
|
||||||
record.location_name,
|
ws.cell(row=row_num, column=7, value=record.action_description).border = border
|
||||||
record.action_description,
|
ws.cell(row=row_num, column=8, value=record.event_description or '').border = border
|
||||||
record.event_description or '',
|
ws.cell(row=row_num, column=9, value=record.recorded_address or '').border = border
|
||||||
record.recorded_address or ''
|
|
||||||
]
|
|
||||||
|
|
||||||
for col_num, value in enumerate(data, 1):
|
# Auto-size columns
|
||||||
cell = ws.cell(row=row_num, column=col_num)
|
for col in ws.columns:
|
||||||
cell.value = value
|
max_length = 0
|
||||||
cell.border = border
|
column = col[0].column_letter
|
||||||
|
for cell in col:
|
||||||
|
try:
|
||||||
|
if len(str(cell.value)) > max_length:
|
||||||
|
max_length = len(str(cell.value))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
adjusted_width = min(max_length + 2, 50)
|
||||||
|
ws.column_dimensions[column].width = adjusted_width
|
||||||
|
|
||||||
# Special formatting for specific columns
|
# Save to BytesIO
|
||||||
if col_num in [5, 6]: # Date and Time columns
|
|
||||||
cell.alignment = Alignment(horizontal='center')
|
|
||||||
elif col_num in [1, 2]: # ID columns
|
|
||||||
cell.alignment = Alignment(horizontal='right')
|
|
||||||
|
|
||||||
# Adjust column widths
|
|
||||||
column_widths = {
|
|
||||||
'A': 8, # ID
|
|
||||||
'B': 12, # Employee ID
|
|
||||||
'C': 20, # Employee Name
|
|
||||||
'D': 15, # Platform
|
|
||||||
'E': 12, # Date
|
|
||||||
'F': 10, # Time
|
|
||||||
'G': 20, # Location Name
|
|
||||||
'H': 18, # Action Description
|
|
||||||
'I': 25, # Event Description
|
|
||||||
'J': 30, # Recorded Address
|
|
||||||
'K': 15, # Import Batch ID
|
|
||||||
'L': 18, # Import Date
|
|
||||||
'M': 25 # Import Source
|
|
||||||
}
|
|
||||||
|
|
||||||
for col, width in column_widths.items():
|
|
||||||
ws.column_dimensions[col].width = width
|
|
||||||
|
|
||||||
# Freeze header row
|
|
||||||
ws.freeze_panes = 'A2'
|
|
||||||
|
|
||||||
# Add summary sheet
|
|
||||||
ws_summary = wb.create_sheet("Summary")
|
|
||||||
|
|
||||||
# Summary statistics
|
|
||||||
total_records = len(records)
|
|
||||||
unique_employees = len(set(r.employee_id for r in records))
|
|
||||||
unique_locations = len(set(r.location_name for r in records))
|
|
||||||
date_range_start = min(r.attendance_date for r in records if r.attendance_date)
|
|
||||||
date_range_end = max(r.attendance_date for r in records if r.attendance_date)
|
|
||||||
|
|
||||||
# Action breakdown
|
|
||||||
action_counts = {}
|
|
||||||
for record in records:
|
|
||||||
action = record.action_description
|
|
||||||
action_counts[action] = action_counts.get(action, 0) + 1
|
|
||||||
|
|
||||||
# Write summary
|
|
||||||
summary_data = [
|
|
||||||
['Time Attendance Export Summary'],
|
|
||||||
[''],
|
|
||||||
['Export Date:', datetime.now().strftime('%Y-%m-%d %H:%M:%S')],
|
|
||||||
['Exported By:', session.get('username', 'Unknown')],
|
|
||||||
[''],
|
|
||||||
['Statistics'],
|
|
||||||
['Total Records:', total_records],
|
|
||||||
['Unique Employees:', unique_employees],
|
|
||||||
['Unique Locations:', unique_locations],
|
|
||||||
['Date Range:', f"{date_range_start.strftime('%Y-%m-%d')} to {date_range_end.strftime('%Y-%m-%d')}"],
|
|
||||||
[''],
|
|
||||||
['Actions Breakdown']
|
|
||||||
]
|
|
||||||
|
|
||||||
for action, count in sorted(action_counts.items()):
|
|
||||||
summary_data.append([action, count])
|
|
||||||
|
|
||||||
for row_num, row_data in enumerate(summary_data, 1):
|
|
||||||
for col_num, value in enumerate(row_data, 1):
|
|
||||||
cell = ws_summary.cell(row=row_num, column=col_num)
|
|
||||||
cell.value = value
|
|
||||||
|
|
||||||
# Style the title
|
|
||||||
if row_num == 1:
|
|
||||||
cell.font = Font(bold=True, size=14)
|
|
||||||
elif row_num in [6, 12]: # Section headers
|
|
||||||
cell.font = Font(bold=True, size=11)
|
|
||||||
|
|
||||||
# Adjust summary column widths
|
|
||||||
ws_summary.column_dimensions['A'].width = 25
|
|
||||||
ws_summary.column_dimensions['B'].width = 15
|
|
||||||
|
|
||||||
# Save to bytes
|
|
||||||
output = io.BytesIO()
|
output = io.BytesIO()
|
||||||
wb.save(output)
|
wb.save(output)
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
|
|
||||||
filename = f'time_attendance_{filter_str}_{timestamp}.xlsx'
|
# New filename format
|
||||||
|
filename = f'time_attendance_{date_range_str}.xlsx'
|
||||||
|
|
||||||
return send_file(
|
return send_file(
|
||||||
output,
|
output,
|
||||||
|
|||||||
Reference in New Issue
Block a user