Automation Part 1
This commit is contained in:
313
backend/igny8_core/business/automation/views.py
Normal file
313
backend/igny8_core/business/automation/views.py
Normal file
@@ -0,0 +1,313 @@
|
||||
"""
|
||||
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)
|
||||
})
|
||||
Reference in New Issue
Block a user