fix(importing): normalize non-breaking spaces in CSV datetime fields
Google Sheets embeds \xa0 (non-breaking space) in 12-hour time values (e.g. "3:00\xa0PM") and when date/time columns are combined. This caused MariaDB datetime INSERTs to fail with an OperationalError. Adds _clean_datetime() which strips \xa0, normalizes whitespace, and parses common import formats (M/D/YYYY H:MM AM/PM, etc.) into YYYY-MM-DD HH:MM:SS before the DB write. Applied to all four datetime fields: session and presentation start/end. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,21 @@ from app.models.response_models import Resp_Body_Base, mk_resp
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _clean_datetime(value) -> str | None:
|
||||
"""Normalize datetime strings from CSV imports (handles \xa0 from Excel, 12-hour format)."""
|
||||
if not value:
|
||||
return None
|
||||
cleaned = str(value).replace('\xa0', ' ').strip()
|
||||
if not cleaned:
|
||||
return None
|
||||
for fmt in ('%m/%d/%Y %I:%M %p', '%m/%d/%Y %H:%M', '%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M'):
|
||||
try:
|
||||
return datetime.datetime.strptime(cleaned, fmt).strftime('%Y-%m-%d %H:%M:%S')
|
||||
except ValueError:
|
||||
continue
|
||||
return cleaned
|
||||
|
||||
|
||||
# No longer needed? 2024-08-15
|
||||
# Based on the program import template the clients are given.
|
||||
# Ideally the import file should only contain records with new External IDs. Old records will be checked and only updated if needed.
|
||||
@@ -332,7 +347,10 @@ router = APIRouter()
|
||||
# ### BEGIN ### Event Importing ### event_importing_program_data() ###
|
||||
# Based on the program import template the clients are given.
|
||||
# Create and update locations, sessions, presentations, and presenters as needed.
|
||||
# Updated 2024-03-25
|
||||
# Careful with how date and time fields are combined
|
||||
# This should work: =TEXT(G2,"M/D/YYYY")&" "&TEXT(H2,"H:MM AM/PM")
|
||||
# Simply adding the fields (=D264+E264) sort of works. This produces non breaking spaces but clean up on import.
|
||||
# Updated 2026-05-15
|
||||
@router.post('/event/{event_id}/importing/program_data', response_model=Resp_Body_Base)
|
||||
async def event_importing_program_data(
|
||||
event_id: str = Path(min_length=11, max_length=22),
|
||||
@@ -656,13 +674,8 @@ async def event_importing_program_data(
|
||||
if record.get('session_description'):
|
||||
event_session_data['description'] = record.get('session_description', '').strip()
|
||||
|
||||
event_session_data['start_datetime'] = record.get('session_start_datetime', '').strip()
|
||||
# event_session_start_datetime = record.get('event_session_start_date', '') + ' ' + record.get('event_session_start_time', '')
|
||||
# event_session_data['start_datetime'] = event_session_start_datetime
|
||||
|
||||
event_session_data['end_datetime'] = record.get('session_end_datetime', '').strip()
|
||||
# event_session_end_datetime = record.get('event_session_end_date', '') + ' ' + record.get('event_session_end_time', '')
|
||||
# event_session_data['end_datetime'] = event_session_end_datetime
|
||||
event_session_data['start_datetime'] = _clean_datetime(record.get('session_start_datetime'))
|
||||
event_session_data['end_datetime'] = _clean_datetime(record.get('session_end_datetime'))
|
||||
|
||||
event_session_data['sort'] = record.get('session_sort')
|
||||
|
||||
@@ -736,19 +749,11 @@ async def event_importing_program_data(
|
||||
if record.get('presentation_description'):
|
||||
event_presentation_data['description'] = record.get('presentation_description', '').strip()
|
||||
|
||||
if record.get('presentation_start_datetime'):
|
||||
event_presentation_data['start_datetime'] = record.get('presentation_start_datetime', '').strip()
|
||||
data['presentation_start_datetime'] = event_presentation_data['start_datetime']
|
||||
else:
|
||||
event_presentation_data['start_datetime'] = None
|
||||
data['presentation_start_datetime'] = None
|
||||
event_presentation_data['start_datetime'] = _clean_datetime(record.get('presentation_start_datetime'))
|
||||
data['presentation_start_datetime'] = event_presentation_data['start_datetime']
|
||||
|
||||
if record.get('presentation_end_datetime'):
|
||||
event_presentation_data['end_datetime'] = record.get('presentation_end_datetime', '').strip()
|
||||
data['presentation_end_datetime'] = event_presentation_data['end_datetime']
|
||||
else:
|
||||
event_presentation_data['end_datetime'] = None
|
||||
data['presentation_end_datetime'] = None
|
||||
event_presentation_data['end_datetime'] = _clean_datetime(record.get('presentation_end_datetime'))
|
||||
data['presentation_end_datetime'] = event_presentation_data['end_datetime']
|
||||
|
||||
if record.get('presentation_abstract_code'):
|
||||
event_presentation_data['abstract_code'] = record.get('presentation_abstract_code', '').strip()
|
||||
|
||||
Reference in New Issue
Block a user