Csv import now works with preacknowlegdments

This commit is contained in:
Jonas Linter
2025-11-18 19:25:52 +01:00
parent e5abefe690
commit 8e2de0fa94
5 changed files with 57 additions and 20 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -39,7 +39,7 @@ from .alpinebits_server import (
Version,
)
from .auth import generate_unique_id, validate_api_key
from .config_loader import load_config
from .config_loader import load_config, get_username_for_hotel
from .const import CONF_GOOGLE_ACCOUNT, CONF_HOTEL_ID, CONF_META_ACCOUNT, HttpStatusCode
from .conversion_service import ConversionService
from .csv_import import CSVImporter
@@ -1142,15 +1142,25 @@ async def handle_wix_form_test(
async def _process_csv_import_background(
csv_content: str,
filename: str,
hotel_code: str | None,
hotel_code: str,
session_maker: SessionMaker,
config: dict[str, Any],
log_filename: Path,
):
"""Background task to process CSV import.
"""Background task to process CSV import with automatic acknowledgement.
This runs in a separate asyncio task after the HTTP response is sent.
Handles both file saving and database processing.
All imported reservations are automatically acknowledged using the username
associated with the hotel_code from the config.
Args:
csv_content: CSV content as string
filename: Original filename
hotel_code: Hotel code (mandatory) - used to get username for acknowledgements
session_maker: SessionMaker for creating database sessions
config: Application configuration
log_filename: Path to save the CSV file
"""
try:
# First, save the CSV file (in background)
@@ -1160,11 +1170,22 @@ async def _process_csv_import_background(
# Now process the CSV import
_LOGGER.info("Starting database processing of %s", filename)
# Get username for acknowledgements from config
username = get_username_for_hotel(config, hotel_code)
# Create a new session for this background task
db_session = await session_maker.create_session()
try:
importer = CSVImporter(db_session, config)
stats = await importer.import_csv_file(str(log_filename), hotel_code, dryrun=False)
# Import with pre-acknowledgement enabled
stats = await importer.import_csv_file(
str(log_filename),
hotel_code,
dryrun=False,
pre_acknowledge=True,
client_id=hotel_code,
username=username
)
_LOGGER.info(
"CSV import complete for %s: %s", filename, stats
@@ -1177,13 +1198,13 @@ async def _process_csv_import_background(
)
@api_router.put("/admin/import-csv/{filename:path}")
@api_router.put("/admin/import-csv/{hotel_code}/{filename:path}")
@limiter.limit(BURST_RATE_LIMIT)
async def import_csv_endpoint(
request: Request,
background_tasks: BackgroundTasks,
hotel_code: str,
filename: str,
hotel_code: str | None = None,
credentials: tuple = Depends(validate_basic_auth),
db_session=Depends(get_async_session),
session_maker: SessionMaker = Depends(get_session_maker),
@@ -1196,16 +1217,18 @@ async def import_csv_endpoint(
- fbclid/gclid tracking IDs
Returns immediately with 202 Accepted while processing continues in background.
All imported reservations are automatically acknowledged using the username
associated with the hotel_code in the config.
Requires basic authentication and saves CSV files to log directory.
Supports gzip compression via Content-Encoding header.
Args:
hotel_code: Hotel code (mandatory) - used to get username for acknowledgements
filename: Name for the CSV file (used for logging)
hotel_code: Optional hotel code to override CSV values
credentials: Basic auth credentials
Example: PUT /api/admin/import-csv/reservations.csv
Example: PUT /api/admin/import-csv/39054_001/reservations.csv
"""
try:

View File

@@ -329,3 +329,8 @@ class Config:
# For backward compatibility
def load_config():
return Config().config
def get_username_for_hotel(config: dict, hotel_code: str) -> str:
"""Get the username associated with a hotel_code from config."""
return next(h.get("username") for h in config.get("alpine_bits_auth", []) if h.get("hotel_id") == hotel_code)

View File

@@ -116,6 +116,21 @@ class CSVImporter:
self.customer_service = CustomerService(db_session)
self.reservation_service = ReservationService(db_session)
def _get_hotel_info(self, hotel_code: str) -> tuple[str, str]:
"""Get hotel name from config by hotel_code.
Args:
hotel_code: Hotel code to look up
Returns:
Tuple of (hotel_code, hotel_name) from config
"""
for hotel in self.config.get("alpine_bits_auth", []):
if hotel.get("hotel_id") == hotel_code:
return hotel_code, hotel.get("hotel_name", "")
# Fallback to default if not found
return hotel_code, self.config.get("default_hotel_name", "Frangart Inn")
async def find_duplicate_reservation(
self,
first_name: str,
@@ -184,13 +199,13 @@ class CSVImporter:
return None
async def import_csv_file(
self, csv_file_path: str, hotel_code: Optional[str] = None, dryrun: bool = False, pre_acknowledge: bool = False, client_id: Optional[str] = None, username: Optional[str] = None
self, csv_file_path: str, hotel_code: str, dryrun: bool = False, pre_acknowledge: bool = False, client_id: Optional[str] = None, username: Optional[str] = None
) -> dict[str, Any]:
"""Import reservations from a CSV file.
Args:
csv_file_path: Path to CSV file
hotel_code: Optional hotel code to override CSV values
hotel_code: Hotel code (mandatory) - used to look up hotel name from config
dryrun: If True, parse and print first 10 rows as JSON without importing
pre_acknowledge: If True, pre-acknowledges all imported reservations
client_id: Client ID for pre-acknowledgement (required if pre_acknowledge=True)
@@ -432,16 +447,8 @@ class CSVImporter:
else:
submission_id = f"csv_import_{row_num}_{datetime.now().isoformat()}"
# Determine hotel code and name
final_hotel_code = (
hotel_code
or str(row.get("hotel_id", "")).strip()
or self.config.get("default_hotel_code", "123")
)
final_hotel_name = (
str(row.get("hotel_name", "")).strip()
or self.config.get("default_hotel_name", "Frangart Inn")
)
# Determine hotel code and name (from config)
final_hotel_code, final_hotel_name = self._get_hotel_info(hotel_code)
# Parse room type fields if available
room_type_code = str(row.get("room_type_code", "")).strip() or None