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
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', (), {
'id': record.id,
'employee_id': str(record.employee_id),
'check_in_date': record.check_in_date,
'check_in_time': record.check_in_time,
'location_name': record.location_name or 'Unknown Location',
'latitude': record.latitude,
'longitude': record.longitude,
'qr_code': record.qr_code
'check_in_date': record.attendance_date,
'check_in_time': record.attendance_time,
'location_name': record.location_name,
'latitude': None,
'longitude': None,
'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)
@@ -7067,7 +7075,7 @@ def download_import_template():
# Define headers
headers = ['ID', 'Name', 'Platform', 'Date', 'Time', 'Location Name',
'Action Description', 'Event Description', 'Recorded Address']
'Action Description', 'Event Description', 'Recorded Address', 'Distance']
# Style headers
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
@@ -7083,11 +7091,9 @@ def download_import_template():
# Add sample data rows
sample_data = [
['12345', 'John Doe', 'iPhone - iOS', '2025-10-06', '09:00:00',
'HQ Suite 210', 'Check In', 'Morning Entry', '123 Main Street'],
['12345', 'John Doe', 'iPhone - iOS', '2025-10-06', '17:30:00',
'HQ Suite 210', 'Check Out', 'Evening Exit', '123 Main Street'],
'HQ Suite 210', 'Check In', 'Main Office', '123 Main St', '0.125'],
['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):
@@ -7124,6 +7130,7 @@ def download_import_template():
["- Platform: Device platform (e.g., iPhone - iOS, Android)"],
["- Event Description: Additional event details"],
["- Recorded Address: Physical address where attendance was recorded"],
["- Distance: Distance in miles between Building and Recorded Address (optional)"],
[""],
["Important Notes:"],
["- 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 '',
record.location_name,
'',
getattr(record.qr_code, 'location_address', '') or '',
getattr(record, 'distance', None) or '',
'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 '',
first_record.location_name,
'',
getattr(first_record.qr_code, 'location_address', '') or '',
getattr(first_record, 'distance', None) or '',
'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 '',
last_record.location_name,
'',
getattr(last_record.qr_code, 'location_address', '') or '',
getattr(last_record, 'distance', None) or '',
'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 '',
check_in_record.location_name,
'',
getattr(check_in_record.qr_code, 'location_address', '') or '',
getattr(check_in_record, 'distance', None) or '',
'No'
]
@@ -7722,13 +7729,13 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
out_time,
last_record.location_name,
'',
'Missed Punch', # Only THIS unpaired record shows Missed Punch
'Missed Punch',
daily_total_display, # Show Daily Total on last row
'',
'',
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'
]
@@ -7759,8 +7766,8 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
'',
'',
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'
]
@@ -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 '',
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'
]
+2
View File
@@ -36,6 +36,8 @@ class TimeAttendance(base.db.Model):
action_description = base.db.Column(base.db.String(100), nullable=False)
event_description = 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_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>Event Description</th>
<th>Recorded Address</th>
<th>Distance</th>
</tr>
</thead>
<tbody>
@@ -326,6 +327,7 @@
<td>Check In</td>
<td>Main Office</td>
<td>123 Main St</td>
<td>0.125</td>
</tr>
<tr>
<td>67890</td>
@@ -337,6 +339,7 @@
<td>Check Out</td>
<td>Main Office</td>
<td>123 Main St</td>
<td>0.125</td>
</tr>
</tbody>
</table>
+59
View File
@@ -157,6 +157,63 @@ class TimeAttendanceImportService:
# Not a hyperlink formula, return as-is
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):
"""
Process recorded address field, handling Excel HYPERLINK formulas
@@ -574,6 +631,7 @@ class TimeAttendanceImportService:
'action_description': str(row['Action Description']).strip(),
'event_description': str(row.get('Event Description', '')).strip() if pd.notna(row.get('Event Description')) else None,
'recorded_address': self._process_recorded_address(row),
'distance': self._parse_distance_field(row),
}
# Check for duplicates
@@ -805,6 +863,7 @@ class TimeAttendanceImportService:
'action_description': record.action_description,
'event_description': record.event_description,
'recorded_address': record.recorded_address,
'distance': getattr(record, 'distance', None),
'import_batch_id': record.import_batch_id,
'import_date': record.import_date,
'import_source': record.import_source