314 lines
11 KiB
Python
314 lines
11 KiB
Python
"""
|
|
Automation API Views
|
|
REST API endpoints for automation management
|
|
"""
|
|
from rest_framework import viewsets, status
|
|
from rest_framework.decorators import action
|
|
from rest_framework.response import Response
|
|
from rest_framework.permissions import IsAuthenticated
|
|
from django.shortcuts import get_object_or_404
|
|
from django.utils import timezone
|
|
|
|
from igny8_core.business.automation.models import AutomationConfig, AutomationRun
|
|
from igny8_core.business.automation.services import AutomationService
|
|
from igny8_core.modules.system.models import Account, Site
|
|
|
|
|
|
class AutomationViewSet(viewsets.ViewSet):
|
|
"""API endpoints for automation"""
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
def _get_site(self, request):
|
|
"""Get site from request"""
|
|
site_id = request.query_params.get('site_id')
|
|
if not site_id:
|
|
return None, Response(
|
|
{'error': 'site_id required'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
site = get_object_or_404(Site, id=site_id, account__user=request.user)
|
|
return site, None
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def config(self, request):
|
|
"""
|
|
GET /api/v1/automation/config/?site_id=123
|
|
Get automation configuration for site
|
|
"""
|
|
site, error_response = self._get_site(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
config, _ = AutomationConfig.objects.get_or_create(
|
|
account=site.account,
|
|
site=site,
|
|
defaults={
|
|
'is_enabled': False,
|
|
'frequency': 'daily',
|
|
'scheduled_time': '02:00',
|
|
}
|
|
)
|
|
|
|
return Response({
|
|
'is_enabled': config.is_enabled,
|
|
'frequency': config.frequency,
|
|
'scheduled_time': str(config.scheduled_time),
|
|
'stage_1_batch_size': config.stage_1_batch_size,
|
|
'stage_2_batch_size': config.stage_2_batch_size,
|
|
'stage_3_batch_size': config.stage_3_batch_size,
|
|
'stage_4_batch_size': config.stage_4_batch_size,
|
|
'stage_5_batch_size': config.stage_5_batch_size,
|
|
'stage_6_batch_size': config.stage_6_batch_size,
|
|
'last_run_at': config.last_run_at,
|
|
'next_run_at': config.next_run_at,
|
|
})
|
|
|
|
@action(detail=False, methods=['put'])
|
|
def update_config(self, request):
|
|
"""
|
|
PUT /api/v1/automation/update_config/?site_id=123
|
|
Update automation configuration
|
|
|
|
Body:
|
|
{
|
|
"is_enabled": true,
|
|
"frequency": "daily",
|
|
"scheduled_time": "02:00",
|
|
"stage_1_batch_size": 20,
|
|
...
|
|
}
|
|
"""
|
|
site, error_response = self._get_site(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
config, _ = AutomationConfig.objects.get_or_create(
|
|
account=site.account,
|
|
site=site
|
|
)
|
|
|
|
# Update fields
|
|
if 'is_enabled' in request.data:
|
|
config.is_enabled = request.data['is_enabled']
|
|
if 'frequency' in request.data:
|
|
config.frequency = request.data['frequency']
|
|
if 'scheduled_time' in request.data:
|
|
config.scheduled_time = request.data['scheduled_time']
|
|
if 'stage_1_batch_size' in request.data:
|
|
config.stage_1_batch_size = request.data['stage_1_batch_size']
|
|
if 'stage_2_batch_size' in request.data:
|
|
config.stage_2_batch_size = request.data['stage_2_batch_size']
|
|
if 'stage_3_batch_size' in request.data:
|
|
config.stage_3_batch_size = request.data['stage_3_batch_size']
|
|
if 'stage_4_batch_size' in request.data:
|
|
config.stage_4_batch_size = request.data['stage_4_batch_size']
|
|
if 'stage_5_batch_size' in request.data:
|
|
config.stage_5_batch_size = request.data['stage_5_batch_size']
|
|
if 'stage_6_batch_size' in request.data:
|
|
config.stage_6_batch_size = request.data['stage_6_batch_size']
|
|
|
|
config.save()
|
|
|
|
return Response({'message': 'Config updated'})
|
|
|
|
@action(detail=False, methods=['post'])
|
|
def run_now(self, request):
|
|
"""
|
|
POST /api/v1/automation/run_now/?site_id=123
|
|
Trigger automation run immediately
|
|
"""
|
|
site, error_response = self._get_site(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
try:
|
|
service = AutomationService(site.account, site)
|
|
run_id = service.start_automation(trigger_type='manual')
|
|
|
|
# Start async processing
|
|
from igny8_core.business.automation.tasks import run_automation_task
|
|
run_automation_task.delay(run_id)
|
|
|
|
return Response({
|
|
'run_id': run_id,
|
|
'message': 'Automation started'
|
|
})
|
|
except ValueError as e:
|
|
return Response(
|
|
{'error': str(e)},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
except Exception as e:
|
|
return Response(
|
|
{'error': f'Failed to start automation: {str(e)}'},
|
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def current_run(self, request):
|
|
"""
|
|
GET /api/v1/automation/current_run/?site_id=123
|
|
Get current automation run status
|
|
"""
|
|
site, error_response = self._get_site(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
run = AutomationRun.objects.filter(
|
|
site=site,
|
|
status__in=['running', 'paused']
|
|
).order_by('-started_at').first()
|
|
|
|
if not run:
|
|
return Response({'run': None})
|
|
|
|
return Response({
|
|
'run': {
|
|
'run_id': run.run_id,
|
|
'status': run.status,
|
|
'current_stage': run.current_stage,
|
|
'trigger_type': run.trigger_type,
|
|
'started_at': run.started_at,
|
|
'total_credits_used': run.total_credits_used,
|
|
'stage_1_result': run.stage_1_result,
|
|
'stage_2_result': run.stage_2_result,
|
|
'stage_3_result': run.stage_3_result,
|
|
'stage_4_result': run.stage_4_result,
|
|
'stage_5_result': run.stage_5_result,
|
|
'stage_6_result': run.stage_6_result,
|
|
'stage_7_result': run.stage_7_result,
|
|
}
|
|
})
|
|
|
|
@action(detail=False, methods=['post'])
|
|
def pause(self, request):
|
|
"""
|
|
POST /api/v1/automation/pause/?run_id=abc123
|
|
Pause automation run
|
|
"""
|
|
run_id = request.query_params.get('run_id')
|
|
if not run_id:
|
|
return Response(
|
|
{'error': 'run_id required'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
try:
|
|
service = AutomationService.from_run_id(run_id)
|
|
service.pause_automation()
|
|
return Response({'message': 'Automation paused'})
|
|
except AutomationRun.DoesNotExist:
|
|
return Response(
|
|
{'error': 'Run not found'},
|
|
status=status.HTTP_404_NOT_FOUND
|
|
)
|
|
|
|
@action(detail=False, methods=['post'])
|
|
def resume(self, request):
|
|
"""
|
|
POST /api/v1/automation/resume/?run_id=abc123
|
|
Resume paused automation run
|
|
"""
|
|
run_id = request.query_params.get('run_id')
|
|
if not run_id:
|
|
return Response(
|
|
{'error': 'run_id required'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
try:
|
|
service = AutomationService.from_run_id(run_id)
|
|
service.resume_automation()
|
|
|
|
# Resume async processing
|
|
from igny8_core.business.automation.tasks import resume_automation_task
|
|
resume_automation_task.delay(run_id)
|
|
|
|
return Response({'message': 'Automation resumed'})
|
|
except AutomationRun.DoesNotExist:
|
|
return Response(
|
|
{'error': 'Run not found'},
|
|
status=status.HTTP_404_NOT_FOUND
|
|
)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def history(self, request):
|
|
"""
|
|
GET /api/v1/automation/history/?site_id=123
|
|
Get automation run history
|
|
"""
|
|
site, error_response = self._get_site(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
runs = AutomationRun.objects.filter(
|
|
site=site
|
|
).order_by('-started_at')[:20]
|
|
|
|
return Response({
|
|
'runs': [
|
|
{
|
|
'run_id': run.run_id,
|
|
'status': run.status,
|
|
'trigger_type': run.trigger_type,
|
|
'started_at': run.started_at,
|
|
'completed_at': run.completed_at,
|
|
'total_credits_used': run.total_credits_used,
|
|
'current_stage': run.current_stage,
|
|
}
|
|
for run in runs
|
|
]
|
|
})
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def logs(self, request):
|
|
"""
|
|
GET /api/v1/automation/logs/?run_id=abc123&lines=100
|
|
Get automation run logs
|
|
"""
|
|
run_id = request.query_params.get('run_id')
|
|
if not run_id:
|
|
return Response(
|
|
{'error': 'run_id required'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
try:
|
|
run = AutomationRun.objects.get(run_id=run_id)
|
|
service = AutomationService(run.account, run.site)
|
|
|
|
lines = int(request.query_params.get('lines', 100))
|
|
log_text = service.logger.get_activity_log(
|
|
run_id, run.account.id, run.site.id, lines
|
|
)
|
|
|
|
return Response({
|
|
'run_id': run_id,
|
|
'log': log_text
|
|
})
|
|
except AutomationRun.DoesNotExist:
|
|
return Response(
|
|
{'error': 'Run not found'},
|
|
status=status.HTTP_404_NOT_FOUND
|
|
)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def estimate(self, request):
|
|
"""
|
|
GET /api/v1/automation/estimate/?site_id=123
|
|
Estimate credits needed for automation
|
|
"""
|
|
site, error_response = self._get_site(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
service = AutomationService(site.account, site)
|
|
estimated_credits = service.estimate_credits()
|
|
|
|
return Response({
|
|
'estimated_credits': estimated_credits,
|
|
'current_balance': site.account.credits_balance,
|
|
'sufficient': site.account.credits_balance >= (estimated_credits * 1.2)
|
|
})
|