Tour operator utils
Tour operator utils (English fallback)
Aug. 17, 2025
Posted by admin
Nhom |
Notes |
|
Tour Operators Utils - External Integration System File Overview Purpose: External tour provider API integration and synchronization Location: utils.py Function: Automated tour content aggregation from third-party providers Key Features: Multi-provider support, price markup management, automatic synchronization
|
|
Import Dependencies Analysis import requests from django.conf import settings from django.utils import timezone from decimal import Decimal from datetime import datetime, timedelta import logging from .models import Tour, TourOperator, ExternalTourSync, TourSchedule logger = logging.getLogger(__name__) External Libraries:
Django Components:
Core Architecture |
|
1. ExternalTourProvider (Base Class) class ExternalTourProvider: """Base class for external tour providers"""
def __init__(self, operator_name): self.operator_name = operator_name self.operator = None self.setup_operator() Purpose: Abstract base class for all external tour providers Design Pattern: Template Method Pattern - defines common workflow Operator Setup Method def setup_operator(self): """Create or get the tour operator for this external provider""" operator, created = TourOperator.objects.get_or_create( name=self.operator_name, defaults={ 'operator_type': 'broker', 'description': f'External tours from {self.operator_name}', 'email': f'noreply@{self.operator_name.lower()}.com', 'phone': '+1-000-000-0000', 'address': 'External Provider', 'is_active': True, 'is_verified': True, } ) self.operator = operator Functionality:
Tour Creation/Update Logic def create_or_update_tour(self, tour_data): """Create or update a tour from external data""" external_id = tour_data.get('id') or tour_data.get('external_id')
# Check if tour already exists try: sync_info = ExternalTourSync.objects.get( external_provider=self.operator_name.lower(), external_tour_id=external_id ) tour = sync_info.tour created = False except ExternalTourSync.DoesNotExist: tour = Tour(tour_operator=self.operator) created = True Business Logic:
Tour Data Mapping # Update tour fields tour.title = tour_data.get('title', '')[:200] tour.title_vi = tour_data.get('title_vi', '')[:200] tour.slug = self.generate_slug(tour.title, external_id) tour.description = tour_data.get('description', '') tour.short_description = tour_data.get('short_description', tour.description[:500]) tour.base_price = Decimal(str(tour_data.get('price', 0))) tour.duration_days = tour_data.get('duration_days', 1) tour.duration_hours = tour_data.get('duration_hours', 0) tour.min_participants = tour_data.get('min_participants', 1) tour.max_participants = tour_data.get('max_participants', 20) tour.source = self.operator_name.lower() tour.external_id = external_id tour.external_url = tour_data.get('url', '') tour.last_synced = timezone.now() Key Features:
Synchronization Record Management # Create or update sync info if created: ExternalTourSync.objects.create( tour=tour, external_provider=self.operator_name.lower(), external_tour_id=external_id, last_sync_date=timezone.now(), external_price=tour.base_price, markup_percentage=Decimal('15.00') # Default markup ) else: sync_info.last_sync_date = timezone.now() sync_info.external_price = tour.base_price sync_info.save() Functionality:
Unique Slug Generation def generate_slug(self, title, external_id): """Generate a unique slug for the tour""" from django.utils.text import slugify base_slug = slugify(title)[:180] # Leave room for suffix slug = f"{base_slug}-{external_id}"
# Ensure uniqueness counter = 1 original_slug = slug while Tour.objects.filter(slug=slug).exists(): slug = f"{original_slug}-{counter}" counter += 1
return slug Features:
|
|
2. ViatorProvider Implementation class ViatorProvider(ExternalTourProvider): """Viator tour provider integration"""
def __init__(self): super().__init__('Viator') self.api_key = getattr(settings, 'VIATOR_API_KEY', '') self.base_url = 'https://api.viator.com/partner' Purpose: Integration with Viator's tour marketplace API Configuration: Uses Django settings for API credentials API Communication def fetch_tours(self, destination_code=None, limit=50): """Fetch tours from Viator API""" if not self.api_key: logger.warning('Viator API key not configured') return []
headers = { 'exp-api-key': self.api_key, 'Accept': 'application/json', 'Accept-Language': 'en-US' }
# Search for products search_url = f"{self.base_url}/products/search" params = { 'count': limit, 'currency': 'USD' }
if destination_code: params['destId'] = destination_code Features:
Data Parsing def parse_viator_product(self, product): """Parse Viator product data into our tour format""" try: return { 'id': product.get('productCode'), 'title': product.get('title', ''), 'description': product.get('description', ''), 'short_description': product.get('shortDescription', ''), 'price': product.get('pricing', {}).get('summary', {}).get('fromPrice', 0), 'duration_days': self.parse_duration(product.get('duration')), 'duration_hours': 0, # Viator usually specifies in days or hours 'min_participants': 1, 'max_participants': 20, 'url': f"https://www.viator.com/tours/{product.get('productCode')}", 'schedules': self.parse_viator_schedules(product) } except Exception as e: logger.error(f'Error parsing Viator product {product.get("productCode")}: {e}') return None Data Transformation:
Duration Parsing def parse_duration(self, duration_str): """Parse Viator duration string to days""" if not duration_str: return 1
# Simple parsing - improve as needed duration_str = duration_str.lower() if 'day' in duration_str: try: return int(duration_str.split()[0]) except (ValueError, IndexError): return 1 return 1 Functionality:
|
|
3. GetYourGuideProvider Implementation class GetYourGuideProvider(ExternalTourProvider): """GetYourGuide tour provider integration"""
def __init__(self): super().__init__('GetYourGuide') self.api_key = getattr(settings, 'GETYOURGUIDE_API_KEY', '') self.base_url = 'https://api.getyourguide.com' Purpose: Integration with GetYourGuide's activity marketplace Similar Structure: Follows same pattern as Viator for consistency Activity Data Parsing def parse_getyourguide_activity(self, activity): """Parse GetYourGuide activity data into our tour format""" try: return { 'id': activity.get('id'), 'title': activity.get('title', ''), 'description': activity.get('description', ''), 'short_description': activity.get('summary', ''), 'price': activity.get('pricing', {}).get('from_price', 0), 'duration_days': 1, # Most GetYourGuide activities are day tours 'duration_hours': activity.get('duration', {}).get('hours', 4), 'min_participants': 1, 'max_participants': activity.get('group_size', {}).get('max', 20), 'url': activity.get('url', ''), 'schedules': self.parse_getyourguide_schedules(activity) } except Exception as e: logger.error(f'Error parsing GetYourGuide activity {activity.get("id")}: {e}') return None GetYourGuide Specifics:
|
|
4. Utility Functions Synchronization Control def sync_external_tours(provider_name, **kwargs): """Sync tours from external providers""" provider_map = { 'viator': ViatorProvider, 'getyourguide': GetYourGuideProvider, }
if provider_name not in provider_map: raise ValueError(f'Unknown provider: {provider_name}')
provider = provider_map[provider_name]() tours_data = provider.fetch_tours(**kwargs)
created_count = 0 updated_count = 0
for tour_data in tours_data: try: tour, created = provider.create_or_update_tour(tour_data) if created: created_count += 1 else: updated_count += 1
logger.info(f'{"Created" if created else "Updated"} tour: {tour.title}')
except Exception as e: logger.error(f'Error processing tour {tour_data.get("id")}: {e}')
return { 'created': created_count, 'updated': updated_count, 'total': len(tours_data) } Features:
Price Management def calculate_markup_price(external_price, markup_percentage): """Calculate the selling price with markup""" markup_amount = external_price * (markup_percentage / 100) return external_price + markup_amount def update_external_tour_prices(): """Update prices for all external tours based on current markup settings""" sync_records = ExternalTourSync.objects.filter(sync_status='active')
for sync_record in sync_records: if sync_record.external_price and sync_record.markup_percentage: new_price = calculate_markup_price( sync_record.external_price, sync_record.markup_percentage )
tour = sync_record.tour tour.base_price = new_price tour.save(update_fields=['base_price', 'updated_at'])
logger.info(f'Updated price for tour {tour.title}: {new_price}') Business Logic:
Comprehensive Synchronization def sync_all_external_tours(): """Sync tours from all configured external providers""" results = {}
providers = ['viator', 'getyourguide']
for provider in providers: try: result = sync_external_tours(provider) results[provider] = result logger.info(f'Synced {result["created"]} new tours from {provider}') except Exception as e: logger.error(f'Error syncing from {provider}: {e}') results[provider] = {'error': str(e)}
return results System Integration:
|
|
Business Use Cases Content Aggregation
Revenue Generation
Operational Efficiency
Integration Architecture
Technical Benefits Data Consistency
Performance Optimization
Error Resilience
This utility system provides a sophisticated, enterprise-grade solution for aggregating and managing external tour content, enabling the platform to offer comprehensive tour inventory while maintaining pricing control and operational efficiency.
|
Nhom |
Notes |
|
Tour Operators Utils - External Integration System File Overview Purpose: External tour provider API integration and synchronization Location: utils.py Function: Automated tour content aggregation from third-party providers Key Features: Multi-provider support, price markup management, automatic synchronization
|
|
Import Dependencies Analysis import requests from django.conf import settings from django.utils import timezone from decimal import Decimal from datetime import datetime, timedelta import logging from .models import Tour, TourOperator, ExternalTourSync, TourSchedule logger = logging.getLogger(__name__) External Libraries:
Django Components:
Core Architecture |
|
1. ExternalTourProvider (Base Class) class ExternalTourProvider: """Base class for external tour providers"""
def __init__(self, operator_name): self.operator_name = operator_name self.operator = None self.setup_operator() Purpose: Abstract base class for all external tour providers Design Pattern: Template Method Pattern - defines common workflow Operator Setup Method def setup_operator(self): """Create or get the tour operator for this external provider""" operator, created = TourOperator.objects.get_or_create( name=self.operator_name, defaults={ 'operator_type': 'broker', 'description': f'External tours from {self.operator_name}', 'email': f'noreply@{self.operator_name.lower()}.com', 'phone': '+1-000-000-0000', 'address': 'External Provider', 'is_active': True, 'is_verified': True, } ) self.operator = operator Functionality:
Tour Creation/Update Logic def create_or_update_tour(self, tour_data): """Create or update a tour from external data""" external_id = tour_data.get('id') or tour_data.get('external_id')
# Check if tour already exists try: sync_info = ExternalTourSync.objects.get( external_provider=self.operator_name.lower(), external_tour_id=external_id ) tour = sync_info.tour created = False except ExternalTourSync.DoesNotExist: tour = Tour(tour_operator=self.operator) created = True Business Logic:
Tour Data Mapping # Update tour fields tour.title = tour_data.get('title', '')[:200] tour.title_vi = tour_data.get('title_vi', '')[:200] tour.slug = self.generate_slug(tour.title, external_id) tour.description = tour_data.get('description', '') tour.short_description = tour_data.get('short_description', tour.description[:500]) tour.base_price = Decimal(str(tour_data.get('price', 0))) tour.duration_days = tour_data.get('duration_days', 1) tour.duration_hours = tour_data.get('duration_hours', 0) tour.min_participants = tour_data.get('min_participants', 1) tour.max_participants = tour_data.get('max_participants', 20) tour.source = self.operator_name.lower() tour.external_id = external_id tour.external_url = tour_data.get('url', '') tour.last_synced = timezone.now() Key Features:
Synchronization Record Management # Create or update sync info if created: ExternalTourSync.objects.create( tour=tour, external_provider=self.operator_name.lower(), external_tour_id=external_id, last_sync_date=timezone.now(), external_price=tour.base_price, markup_percentage=Decimal('15.00') # Default markup ) else: sync_info.last_sync_date = timezone.now() sync_info.external_price = tour.base_price sync_info.save() Functionality:
Unique Slug Generation def generate_slug(self, title, external_id): """Generate a unique slug for the tour""" from django.utils.text import slugify base_slug = slugify(title)[:180] # Leave room for suffix slug = f"{base_slug}-{external_id}"
# Ensure uniqueness counter = 1 original_slug = slug while Tour.objects.filter(slug=slug).exists(): slug = f"{original_slug}-{counter}" counter += 1
return slug Features:
|
|
2. ViatorProvider Implementation class ViatorProvider(ExternalTourProvider): """Viator tour provider integration"""
def __init__(self): super().__init__('Viator') self.api_key = getattr(settings, 'VIATOR_API_KEY', '') self.base_url = 'https://api.viator.com/partner' Purpose: Integration with Viator's tour marketplace API Configuration: Uses Django settings for API credentials API Communication def fetch_tours(self, destination_code=None, limit=50): """Fetch tours from Viator API""" if not self.api_key: logger.warning('Viator API key not configured') return []
headers = { 'exp-api-key': self.api_key, 'Accept': 'application/json', 'Accept-Language': 'en-US' }
# Search for products search_url = f"{self.base_url}/products/search" params = { 'count': limit, 'currency': 'USD' }
if destination_code: params['destId'] = destination_code Features:
Data Parsing def parse_viator_product(self, product): """Parse Viator product data into our tour format""" try: return { 'id': product.get('productCode'), 'title': product.get('title', ''), 'description': product.get('description', ''), 'short_description': product.get('shortDescription', ''), 'price': product.get('pricing', {}).get('summary', {}).get('fromPrice', 0), 'duration_days': self.parse_duration(product.get('duration')), 'duration_hours': 0, # Viator usually specifies in days or hours 'min_participants': 1, 'max_participants': 20, 'url': f"https://www.viator.com/tours/{product.get('productCode')}", 'schedules': self.parse_viator_schedules(product) } except Exception as e: logger.error(f'Error parsing Viator product {product.get("productCode")}: {e}') return None Data Transformation:
Duration Parsing def parse_duration(self, duration_str): """Parse Viator duration string to days""" if not duration_str: return 1
# Simple parsing - improve as needed duration_str = duration_str.lower() if 'day' in duration_str: try: return int(duration_str.split()[0]) except (ValueError, IndexError): return 1 return 1 Functionality:
|
|
3. GetYourGuideProvider Implementation class GetYourGuideProvider(ExternalTourProvider): """GetYourGuide tour provider integration"""
def __init__(self): super().__init__('GetYourGuide') self.api_key = getattr(settings, 'GETYOURGUIDE_API_KEY', '') self.base_url = 'https://api.getyourguide.com' Purpose: Integration with GetYourGuide's activity marketplace Similar Structure: Follows same pattern as Viator for consistency Activity Data Parsing def parse_getyourguide_activity(self, activity): """Parse GetYourGuide activity data into our tour format""" try: return { 'id': activity.get('id'), 'title': activity.get('title', ''), 'description': activity.get('description', ''), 'short_description': activity.get('summary', ''), 'price': activity.get('pricing', {}).get('from_price', 0), 'duration_days': 1, # Most GetYourGuide activities are day tours 'duration_hours': activity.get('duration', {}).get('hours', 4), 'min_participants': 1, 'max_participants': activity.get('group_size', {}).get('max', 20), 'url': activity.get('url', ''), 'schedules': self.parse_getyourguide_schedules(activity) } except Exception as e: logger.error(f'Error parsing GetYourGuide activity {activity.get("id")}: {e}') return None GetYourGuide Specifics:
|
|
4. Utility Functions Synchronization Control def sync_external_tours(provider_name, **kwargs): """Sync tours from external providers""" provider_map = { 'viator': ViatorProvider, 'getyourguide': GetYourGuideProvider, }
if provider_name not in provider_map: raise ValueError(f'Unknown provider: {provider_name}')
provider = provider_map[provider_name]() tours_data = provider.fetch_tours(**kwargs)
created_count = 0 updated_count = 0
for tour_data in tours_data: try: tour, created = provider.create_or_update_tour(tour_data) if created: created_count += 1 else: updated_count += 1
logger.info(f'{"Created" if created else "Updated"} tour: {tour.title}')
except Exception as e: logger.error(f'Error processing tour {tour_data.get("id")}: {e}')
return { 'created': created_count, 'updated': updated_count, 'total': len(tours_data) } Features:
Price Management def calculate_markup_price(external_price, markup_percentage): """Calculate the selling price with markup""" markup_amount = external_price * (markup_percentage / 100) return external_price + markup_amount def update_external_tour_prices(): """Update prices for all external tours based on current markup settings""" sync_records = ExternalTourSync.objects.filter(sync_status='active')
for sync_record in sync_records: if sync_record.external_price and sync_record.markup_percentage: new_price = calculate_markup_price( sync_record.external_price, sync_record.markup_percentage )
tour = sync_record.tour tour.base_price = new_price tour.save(update_fields=['base_price', 'updated_at'])
logger.info(f'Updated price for tour {tour.title}: {new_price}') Business Logic:
Comprehensive Synchronization def sync_all_external_tours(): """Sync tours from all configured external providers""" results = {}
providers = ['viator', 'getyourguide']
for provider in providers: try: result = sync_external_tours(provider) results[provider] = result logger.info(f'Synced {result["created"]} new tours from {provider}') except Exception as e: logger.error(f'Error syncing from {provider}: {e}') results[provider] = {'error': str(e)}
return results System Integration:
|
|
Business Use Cases Content Aggregation
Revenue Generation
Operational Efficiency
Integration Architecture
Technical Benefits Data Consistency
Performance Optimization
Error Resilience
This utility system provides a sophisticated, enterprise-grade solution for aggregating and managing external tour content, enabling the platform to offer comprehensive tour inventory while maintaining pricing control and operational efficiency.
|
Attached Files
You are viewing this article in public mode. Some features may be limited.