import json from datetime import datetime import logging from typing import Dict, Any from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from django.utils.decorators import method_decorator from django.views import View from django.core.exceptions import ValidationError from django.db import transaction from .models import Source, JobPosting, IntegrationLog from .erp_integration_service import ERPIntegrationService class ERPIntegrationView(View): """ API endpoint for receiving job requests from ERP system """ def get(self, request): """Health check endpoint""" return JsonResponse({ 'status': 'success', 'message': 'ERP Integration API is available', 'version': '1.0.0', 'supported_actions': ['create_job', 'update_job'] }) @method_decorator(csrf_exempt) def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) def post(self, request): """Handle POST requests from ERP system""" try: # Start timing for processing start_time = datetime.now() # Get request data if request.content_type == 'application/json': try: data = json.loads(request.body.decode('utf-8')) except json.JSONDecodeError: return JsonResponse({ 'status': 'error', 'message': 'Invalid JSON data' }, status=400) else: data = request.POST.dict() # Get action from request action = data.get('action', '').lower() if not action: return JsonResponse({ 'status': 'error', 'message': 'Action is required' }, status=400) # Validate action valid_actions = ['create_job', 'update_job'] if action not in valid_actions: return JsonResponse({ 'status': 'error', 'message': f'Invalid action. Must be one of: {", ".join(valid_actions)}' }, status=400) # Get source identifier source_name = data.get('source_name') source_id = data.get('source_id') # Find the source source = None if source_id: source = Source.objects.filter(id=source_id).first() elif source_name: source = Source.objects.filter(name=source_name).first() if not source: return JsonResponse({ 'status': 'error', 'message': 'Source not found' }, status=404) job_id = data.get('job_id') if not job_id: return JsonResponse({ 'status': 'error', 'message': 'Job ID is required and must be unique' }) if JobPosting.objects.filter(internal_job_id=job_id).exists(): return JsonResponse({ 'status': 'error', 'message': 'Job with this ID already exists' }, status=400) # Create integration service service = ERPIntegrationService(source) # Validate request is_valid, error_msg = service.validate_request(request) if not is_valid: service.log_integration_request(request, 'ERROR', error_message=error_msg, status_code='403') return JsonResponse({ 'status': 'error', 'message': error_msg }, status=403) # Log the request service.log_integration_request(request, 'REQUEST') # Process based on action if action == 'create_job': result, error_msg = self._create_job(service, data) elif action == 'update_job': result, error_msg = self._update_job(service, data) # Calculate processing time processing_time = (datetime.now() - start_time).total_seconds() # Log the result status_code = '200' if not error_msg else '400' service.log_integration_request( request, 'RESPONSE' if not error_msg else 'ERROR', response_data={'result': result} if result else {}, status_code=status_code, processing_time=processing_time, error_message=error_msg ) # Return response if error_msg: return JsonResponse({ 'status': 'error', 'message': error_msg, 'processing_time': processing_time }, status=400) return JsonResponse({ 'status': 'success', 'message': f'Job {action.replace("_", " ")} successfully', 'data': result, 'processing_time': processing_time }) except Exception as e: logger = logging.getLogger(__name__) logger.error(f"Error in ERP integration: {str(e)}", exc_info=True) return JsonResponse({ 'status': 'error', 'message': 'Internal server error' }, status=500) @transaction.atomic def _create_job(self, service: ERPIntegrationService, data: Dict[str, Any]) -> tuple[Dict[str, Any], str]: """Create a new job from ERP data""" # Validate ERP data # print(data) is_valid, error_msg = service.validate_erp_data(data) if not is_valid: return None, error_msg # Create job from ERP data job, error_msg = service.create_job_from_erp(data) if error_msg: return None, error_msg # Prepare response data response_data = { 'job_id': job.internal_job_id, 'title': job.title, 'status': job.status, 'created_at': job.created_at.isoformat(), 'message': 'Job created successfully' } return response_data, "" @transaction.atomic def _update_job(self, service: ERPIntegrationService, data: Dict[str, Any]) -> tuple[Dict[str, Any], str]: """Update an existing job from ERP data""" # Get job ID from request job_id = data.get('job_id') if not job_id: return None, "Job ID is required for update" # Validate ERP data is_valid, error_msg = service.validate_erp_data(data) if not is_valid: return None, error_msg # Update job from ERP data job, error_msg = service.update_job_from_erp(job_id, data) if error_msg: return None, error_msg # Prepare response data response_data = { 'job_id': job.internal_job_id, 'title': job.title, 'status': job.status, 'updated_at': job.updated_at.isoformat(), 'message': 'Job updated successfully' } return response_data, "" # Specific endpoint for creating jobs (POST only) @require_http_methods(["POST"]) @csrf_exempt def erp_create_job_view(request): """View for creating jobs from ERP (simpler endpoint)""" view = ERPIntegrationView() return view.post(request) # Specific endpoint for updating jobs (POST only) @require_http_methods(["POST"]) @csrf_exempt def erp_update_job_view(request): """View for updating jobs from ERP (simpler endpoint)""" view = ERPIntegrationView() return view.post(request) # Health check endpoint @require_http_methods(["GET"]) def erp_integration_health(request): """Health check endpoint for ERP integration""" return JsonResponse({ 'status': 'healthy', 'timestamp': datetime.now().isoformat(), 'services': { 'erp_integration': 'available', 'database': 'connected' } })