From 07c540c7f9fbc8d00c273cd9c4e498dfcff07c16 Mon Sep 17 00:00:00 2001 From: NguyenND Date: Thu, 9 Apr 2026 11:19:08 -0400 Subject: [PATCH] 04/08 Time Attendance: added checkbox to allow export unlimited date range --- routes/time_attendance.py | 20 +++++++++--------- routes/time_attendance_export.py | 18 ++++++++--------- templates/time_attendance_records.html | 28 ++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/routes/time_attendance.py b/routes/time_attendance.py index 74074db..af4796a 100644 --- a/routes/time_attendance.py +++ b/routes/time_attendance.py @@ -1146,6 +1146,8 @@ def export_time_attendance(): end_date = request.args.get('end_date') import_batch = request.args.get('import_batch') project_filter = request.args.get('project_id') + # unlimited=true skips the 14-day cap; default is capped (2-week) mode + unlimited = request.args.get('unlimited', 'false').lower() == 'true' # Build query with same filters as the view query = TimeAttendance.query @@ -1224,7 +1226,7 @@ def export_time_attendance(): # Log export logger_handler.logger.info( f"User {session['username']} exported {len(records)} time attendance records " - f"in {export_format.upper()} format" + f"in {export_format.upper()} format (unlimited={unlimited})" ) # Format dates for filename (MMDDYYYY format) @@ -1263,7 +1265,7 @@ def export_time_attendance(): filter_str = "_".join(filter_desc) if filter_desc else "all" - return export_time_attendance_excel(records, project_name_for_filename, date_range_str, filter_str, start_date, end_date) + return export_time_attendance_excel(records, project_name_for_filename, date_range_str, filter_str, start_date, end_date, unlimited=unlimited) except Exception as e: logger_handler.logger.error(f"Error exporting time attendance records: {e}") @@ -1291,10 +1293,12 @@ def export_time_attendance_by_building(): end_date = request.args.get('end_date') import_batch = request.args.get('import_batch') project_filter = request.args.get('project_id') - + # unlimited=true skips the 14-day cap; default is capped (2-week) mode + unlimited = request.args.get('unlimited', 'false').lower() == 'true' + # Build query with same filters as the view query = TimeAttendance.query - + # Apply filters — employee_id supports comma-separated multi-employee values if employee_filter: employee_ids_export = [e.strip() for e in employee_filter.split(',') if e.strip()] @@ -1315,8 +1319,6 @@ def export_time_attendance_by_building(): if location_filter: query = query.filter(TimeAttendance.location_name == location_filter) - - if start_date: try: start_date_obj = datetime.strptime(start_date, '%Y-%m-%d').date() query = query.filter(TimeAttendance.attendance_date >= start_date_obj) @@ -1369,7 +1371,7 @@ def export_time_attendance_by_building(): # Log export logger_handler.logger.info( f"User {session['username']} exported {len(records)} time attendance records " - f"by building in Excel format" + f"by building in Excel format (unlimited={unlimited})" ) # Format dates for filename (MMDDYYYY format) @@ -1398,7 +1400,7 @@ def export_time_attendance_by_building(): elif date_to_formatted: date_range_str = f"to_{date_to_formatted}" - return export_time_attendance_by_building_excel(records, project_name_for_filename, date_range_str, start_date, end_date) + return export_time_attendance_by_building_excel(records, project_name_for_filename, date_range_str, start_date, end_date, unlimited=unlimited) except Exception as e: logger_handler.logger.error(f"Error exporting time attendance records by building: {e}") @@ -1690,6 +1692,4 @@ def api_time_attendance_by_location(location_name): 'error': 'Failed to retrieve time attendance records' }), 500 - - # Jinja2 filters for better template functionality \ No newline at end of file diff --git a/routes/time_attendance_export.py b/routes/time_attendance_export.py index 9080c8b..8b98ab2 100644 --- a/routes/time_attendance_export.py +++ b/routes/time_attendance_export.py @@ -102,14 +102,14 @@ def _qtr(decimal_hours: float) -> float: # Private export helpers — shared by both export functions below. # --------------------------------------------------------------------------- -def _resolve_date_range(start_date_filter, end_date_filter, records, export_label='TA Excel export'): +def _resolve_date_range(start_date_filter, end_date_filter, records, export_label='TA Excel export', unlimited=False): """ Resolve and validate the export date range. Returns (start_date, end_date, filtered_records) where: - start_date / end_date are date objects - filtered_records is the input list capped to end_date + 1 day (overnight buffer) - - end_date is capped to a maximum 14-day window + - end_date is capped to a maximum 14-day window unless unlimited=True Returns None when there are no records and no date filters. """ MAX_EXPORT_DAYS = 14 @@ -129,7 +129,7 @@ def _resolve_date_range(start_date_filter, end_date_filter, records, export_labe else: return None - if (end_date - start_date).days >= MAX_EXPORT_DAYS: + if not unlimited and (end_date - start_date).days >= MAX_EXPORT_DAYS: capped_end_date = start_date + timedelta(days=MAX_EXPORT_DAYS - 1) logger_handler.logger.info( f"{export_label}: date range [{start_date} \u2013 {end_date}] exceeds " @@ -272,7 +272,7 @@ def _make_export_styles(): } -def export_time_attendance_excel(records, project_name_for_filename, date_range_str, filter_str, start_date_filter=None, end_date_filter=None): +def export_time_attendance_excel(records, project_name_for_filename, date_range_str, filter_str, start_date_filter=None, end_date_filter=None, unlimited=False): """Generate Excel export with template format matching the provided template""" from openpyxl import Workbook from openpyxl.styles import Font, PatternFill, Border, Side, Alignment @@ -284,8 +284,8 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_ ws = wb.active ws.title = "Sheet0" - # Resolve date range and cap to 14-day window - result = _resolve_date_range(start_date_filter, end_date_filter, records, 'TA Excel export') + # Resolve date range; skip 14-day cap when unlimited=True + result = _resolve_date_range(start_date_filter, end_date_filter, records, 'TA Excel export', unlimited=unlimited) if result is None: return None start_date, end_date, records = result @@ -1345,7 +1345,7 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_ ) -def export_time_attendance_by_building_excel(records, project_name_for_filename, date_range_str, start_date_filter=None, end_date_filter=None): +def export_time_attendance_by_building_excel(records, project_name_for_filename, date_range_str, start_date_filter=None, end_date_filter=None, unlimited=False): """Generate Excel export grouped by building/location with template format""" from openpyxl import Workbook from openpyxl.styles import Font, PatternFill, Border, Side, Alignment @@ -1357,8 +1357,8 @@ def export_time_attendance_by_building_excel(records, project_name_for_filename, ws = wb.active ws.title = "Sheet0" - # Resolve date range and cap to 14-day window - result = _resolve_date_range(start_date_filter, end_date_filter, records, 'TA by-building Excel export') + # Resolve date range; skip 14-day cap when unlimited=True + result = _resolve_date_range(start_date_filter, end_date_filter, records, 'TA by-building Excel export', unlimited=unlimited) if result is None: return None start_date, end_date, records = result diff --git a/templates/time_attendance_records.html b/templates/time_attendance_records.html index b2e9239..3b6027b 100644 --- a/templates/time_attendance_records.html +++ b/templates/time_attendance_records.html @@ -4,6 +4,26 @@ {% block extra_head %}