Fixing distance value in export file

This commit is contained in:
2025-10-15 17:01:55 -04:00
parent df34f67c91
commit 8b5cb40372
4 changed files with 98 additions and 27 deletions
+34 -27
View File
@@ -5737,16 +5737,24 @@ def get_miss_punch_details(employee_id):
# Convert to the format expected by the calculator # Convert to the format expected by the calculator
converted_records = [] converted_records = []
for record in attendance_records: for record in records:
# Get distance from the TimeAttendance record
distance_value = getattr(record, 'distance', None)
converted_record = type('Record', (), { converted_record = type('Record', (), {
'id': record.id, 'id': record.id,
'employee_id': str(record.employee_id), 'employee_id': str(record.employee_id),
'check_in_date': record.check_in_date, 'check_in_date': record.attendance_date,
'check_in_time': record.check_in_time, 'check_in_time': record.attendance_time,
'location_name': record.location_name or 'Unknown Location', 'location_name': record.location_name,
'latitude': record.latitude, 'latitude': None,
'longitude': record.longitude, 'longitude': None,
'qr_code': record.qr_code 'distance': distance_value, # ADD THIS LINE
'qr_code': type('QRCode', (), {
'location': record.location_name,
'location_address': record.recorded_address or '',
'project': None
})()
})() })()
converted_records.append(converted_record) converted_records.append(converted_record)
@@ -7067,7 +7075,7 @@ def download_import_template():
# Define headers # Define headers
headers = ['ID', 'Name', 'Platform', 'Date', 'Time', 'Location Name', headers = ['ID', 'Name', 'Platform', 'Date', 'Time', 'Location Name',
'Action Description', 'Event Description', 'Recorded Address'] 'Action Description', 'Event Description', 'Recorded Address', 'Distance']
# Style headers # Style headers
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid") header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
@@ -7083,11 +7091,9 @@ def download_import_template():
# Add sample data rows # Add sample data rows
sample_data = [ sample_data = [
['12345', 'John Doe', 'iPhone - iOS', '2025-10-06', '09:00:00', ['12345', 'John Doe', 'iPhone - iOS', '2025-10-06', '09:00:00',
'HQ Suite 210', 'Check In', 'Morning Entry', '123 Main Street'], 'HQ Suite 210', 'Check In', 'Main Office', '123 Main St', '0.125'],
['12345', 'John Doe', 'iPhone - iOS', '2025-10-06', '17:30:00',
'HQ Suite 210', 'Check Out', 'Evening Exit', '123 Main Street'],
['67890', 'Jane Smith', 'Android', '2025-10-06', '08:45:00', ['67890', 'Jane Smith', 'Android', '2025-10-06', '08:45:00',
'Branch Office', 'Check In', 'Morning Entry', '456 Oak Avenue'], 'Branch Office', 'Check In', 'Morning Entry', '456 Oak Avenue', '0.250'],
] ]
for row_num, row_data in enumerate(sample_data, 2): for row_num, row_data in enumerate(sample_data, 2):
@@ -7124,6 +7130,7 @@ def download_import_template():
["- Platform: Device platform (e.g., iPhone - iOS, Android)"], ["- Platform: Device platform (e.g., iPhone - iOS, Android)"],
["- Event Description: Additional event details"], ["- Event Description: Additional event details"],
["- Recorded Address: Physical address where attendance was recorded"], ["- Recorded Address: Physical address where attendance was recorded"],
["- Distance: Distance in miles between Building and Recorded Address (optional)"],
[""], [""],
["Important Notes:"], ["Important Notes:"],
["- Do not modify the header row"], ["- Do not modify the header row"],
@@ -7575,8 +7582,8 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
'', '',
'', '',
getattr(record.qr_code, 'location_address', '') or '', getattr(record.qr_code, 'location_address', '') or '',
record.location_name, getattr(record.qr_code, 'location_address', '') or '',
'', getattr(record, 'distance', None) or '',
'No' 'No'
] ]
@@ -7605,8 +7612,8 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
'', '',
'', '',
getattr(first_record.qr_code, 'location_address', '') or '', getattr(first_record.qr_code, 'location_address', '') or '',
first_record.location_name, getattr(first_record.qr_code, 'location_address', '') or '',
'', getattr(first_record, 'distance', None) or '',
'No' 'No'
] ]
@@ -7633,8 +7640,8 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
'', '',
'', '',
getattr(last_record.qr_code, 'location_address', '') or '', getattr(last_record.qr_code, 'location_address', '') or '',
last_record.location_name, getattr(last_record.qr_code, 'location_address', '') or '',
'', getattr(last_record, 'distance', None) or '',
'No' 'No'
] ]
@@ -7674,8 +7681,8 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
'', '',
'', '',
getattr(check_in_record.qr_code, 'location_address', '') or '', getattr(check_in_record.qr_code, 'location_address', '') or '',
check_in_record.location_name, getattr(check_in_record.qr_code, 'location_address', '') or '',
'', getattr(check_in_record, 'distance', None) or '',
'No' 'No'
] ]
@@ -7722,13 +7729,13 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
out_time, out_time,
last_record.location_name, last_record.location_name,
'', '',
'Missed Punch', # Only THIS unpaired record shows Missed Punch 'Missed Punch',
daily_total_display, # Show Daily Total on last row daily_total_display, # Show Daily Total on last row
'', '',
'', '',
getattr(last_record.qr_code, 'location_address', '') or '', getattr(last_record.qr_code, 'location_address', '') or '',
last_record.location_name, getattr(last_record.qr_code, 'location_address', '') or '',
'', getattr(last_record, 'distance', None) or '',
'No' 'No'
] ]
@@ -7759,8 +7766,8 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
'', '',
'', '',
getattr(start_record.qr_code, 'location_address', '') or '', getattr(start_record.qr_code, 'location_address', '') or '',
start_record.location_name, getattr(start_record.qr_code, 'location_address', '') or '',
'', getattr(start_record, 'distance', None) or '',
'No' 'No'
] ]
@@ -7784,8 +7791,8 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
'', '',
'', '',
getattr(start_record.qr_code, 'location_address', '') if start_record else '', getattr(start_record.qr_code, 'location_address', '') if start_record else '',
start_record.location_name if start_record else '', getattr(start_record.qr_code, 'location_address', '') if start_record else '',
'', getattr(start_record, 'distance', None) if start_record else '',
'No' 'No'
] ]
+2
View File
@@ -36,6 +36,8 @@ class TimeAttendance(base.db.Model):
action_description = base.db.Column(base.db.String(100), nullable=False) action_description = base.db.Column(base.db.String(100), nullable=False)
event_description = base.db.Column(base.db.Text, nullable=True) event_description = base.db.Column(base.db.Text, nullable=True)
recorded_address = base.db.Column(base.db.Text, nullable=True) recorded_address = base.db.Column(base.db.Text, nullable=True)
# Distance/Location accuracy field (in miles)
distance = base.db.Column(base.db.Float, nullable=True)
# Import tracking # Import tracking
import_batch_id = base.db.Column(base.db.String(36), nullable=True, index=True) import_batch_id = base.db.Column(base.db.String(36), nullable=True, index=True)
+3
View File
@@ -313,6 +313,7 @@
<th>Action Description</th> <th>Action Description</th>
<th>Event Description</th> <th>Event Description</th>
<th>Recorded Address</th> <th>Recorded Address</th>
<th>Distance</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -326,6 +327,7 @@
<td>Check In</td> <td>Check In</td>
<td>Main Office</td> <td>Main Office</td>
<td>123 Main St</td> <td>123 Main St</td>
<td>0.125</td>
</tr> </tr>
<tr> <tr>
<td>67890</td> <td>67890</td>
@@ -337,6 +339,7 @@
<td>Check Out</td> <td>Check Out</td>
<td>Main Office</td> <td>Main Office</td>
<td>123 Main St</td> <td>123 Main St</td>
<td>0.125</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
+59
View File
@@ -157,6 +157,63 @@ class TimeAttendanceImportService:
# Not a hyperlink formula, return as-is # Not a hyperlink formula, return as-is
return cell_value if cell_value else None return cell_value if cell_value else None
def _parse_distance_field(self, row) -> Optional[float]:
"""
Parse Distance field from Excel (optional column)
Args:
row: DataFrame row containing the 'Distance' column
Returns:
Distance value as float in miles, or None if not present/invalid
"""
# Check if Distance column exists
if 'Distance' not in row.index:
return None
distance_value = row.get('Distance')
# Check if value exists and is not NaN
if pd.isna(distance_value):
return None
# Try to parse as float
try:
# Handle string values
if isinstance(distance_value, str):
distance_str = distance_value.strip()
# Skip empty strings
if not distance_str or distance_str.lower() in ['', 'n/a', 'na', 'none']:
return None
# Remove any text like "miles", "mi", "m"
distance_str = distance_str.lower()
distance_str = distance_str.replace('miles', '').replace('mile', '').replace('mi', '').replace('m', '').strip()
# Parse the number
distance_float = float(distance_str)
else:
# Already a number
distance_float = float(distance_value)
# Validate reasonable range (0 to 100 miles)
if distance_float < 0:
if self.logger:
self.logger.logger.warning(f"Negative distance value {distance_float} converted to positive")
distance_float = abs(distance_float)
if distance_float > 100:
if self.logger:
self.logger.logger.warning(f"Distance value {distance_float} exceeds 100 miles, may be invalid")
return round(distance_float, 4) # Round to 4 decimal places
except (ValueError, TypeError) as e:
if self.logger:
self.logger.logger.debug(f"Could not parse distance value '{distance_value}': {e}")
return None
def _process_recorded_address(self, row): def _process_recorded_address(self, row):
""" """
Process recorded address field, handling Excel HYPERLINK formulas Process recorded address field, handling Excel HYPERLINK formulas
@@ -574,6 +631,7 @@ class TimeAttendanceImportService:
'action_description': str(row['Action Description']).strip(), 'action_description': str(row['Action Description']).strip(),
'event_description': str(row.get('Event Description', '')).strip() if pd.notna(row.get('Event Description')) else None, 'event_description': str(row.get('Event Description', '')).strip() if pd.notna(row.get('Event Description')) else None,
'recorded_address': self._process_recorded_address(row), 'recorded_address': self._process_recorded_address(row),
'distance': self._parse_distance_field(row),
} }
# Check for duplicates # Check for duplicates
@@ -805,6 +863,7 @@ class TimeAttendanceImportService:
'action_description': record.action_description, 'action_description': record.action_description,
'event_description': record.event_description, 'event_description': record.event_description,
'recorded_address': record.recorded_address, 'recorded_address': record.recorded_address,
'distance': getattr(record, 'distance', None),
'import_batch_id': record.import_batch_id, 'import_batch_id': record.import_batch_id,
'import_date': record.import_date, 'import_date': record.import_date,
'import_source': record.import_source 'import_source': record.import_source