HH/apps/ai_engine/views.py
2025-12-24 14:10:18 +03:00

255 lines
7.6 KiB
Python

"""
AI Engine API views
"""
import time
from django.db.models import Q
from drf_spectacular.utils import extend_schema, extend_schema_view
from rest_framework import status, viewsets
from rest_framework.decorators import action, api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from apps.accounts.permissions import IsPXAdmin, IsHospitalAdmin
from .models import SentimentResult
from .serializers import (
AnalyzeTextRequestSerializer,
AnalyzeTextResponseSerializer,
BatchAnalyzeRequestSerializer,
BatchAnalyzeResponseSerializer,
SentimentResultSerializer,
SentimentStatsSerializer,
)
from .services import AIEngineService
@extend_schema_view(
list=extend_schema(
summary="List sentiment results",
description="Get a list of all sentiment analysis results with filtering options"
),
retrieve=extend_schema(
summary="Get sentiment result",
description="Get details of a specific sentiment analysis result"
),
)
class SentimentResultViewSet(viewsets.ReadOnlyModelViewSet):
"""
Sentiment result viewset - Read-only API for sentiment results.
Provides:
- List all sentiment results with filters
- Retrieve specific sentiment result
- Statistics endpoint
"""
serializer_class = SentimentResultSerializer
permission_classes = [IsAuthenticated]
filterset_fields = ['sentiment', 'language', 'ai_service']
search_fields = ['text']
ordering_fields = ['created_at', 'sentiment_score', 'confidence']
ordering = ['-created_at']
def get_queryset(self):
"""Filter queryset based on user permissions"""
queryset = SentimentResult.objects.select_related('content_type').all()
# Apply filters from query params
sentiment = self.request.query_params.get('sentiment')
if sentiment:
queryset = queryset.filter(sentiment=sentiment)
language = self.request.query_params.get('language')
if language:
queryset = queryset.filter(language=language)
ai_service = self.request.query_params.get('ai_service')
if ai_service:
queryset = queryset.filter(ai_service=ai_service)
min_confidence = self.request.query_params.get('min_confidence')
if min_confidence:
queryset = queryset.filter(confidence__gte=min_confidence)
search = self.request.query_params.get('search')
if search:
queryset = queryset.filter(text__icontains=search)
return queryset
@extend_schema(
summary="Get sentiment statistics",
description="Get aggregated statistics for sentiment results",
responses={200: SentimentStatsSerializer}
)
@action(detail=False, methods=['get'])
def stats(self, request):
"""Get sentiment statistics"""
queryset = self.get_queryset()
stats = AIEngineService.get_sentiment_stats(queryset)
serializer = SentimentStatsSerializer(stats)
return Response(serializer.data)
@extend_schema(
summary="Analyze text",
description="Analyze text for sentiment, keywords, entities, and emotions",
request=AnalyzeTextRequestSerializer,
responses={200: AnalyzeTextResponseSerializer}
)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def analyze_text(request):
"""
Analyze text for sentiment.
POST /api/ai-engine/analyze/
Request body:
{
"text": "The service was excellent!",
"language": "en", // optional, auto-detected if not provided
"extract_keywords": true,
"extract_entities": true,
"detect_emotions": true
}
Response:
{
"text": "The service was excellent!",
"language": "en",
"sentiment": "positive",
"sentiment_score": 0.8,
"confidence": 0.9,
"keywords": ["excellent"],
"entities": [],
"emotions": {"joy": 0.6},
"ai_service": "stub",
"ai_model": "keyword_matching_v1",
"processing_time_ms": 5
}
"""
serializer = AnalyzeTextRequestSerializer(data=request.data)
if not serializer.is_valid():
return Response(
serializer.errors,
status=status.HTTP_400_BAD_REQUEST
)
# Perform analysis
result = AIEngineService.sentiment.analyze_text(
text=serializer.validated_data['text'],
language=serializer.validated_data.get('language'),
extract_keywords=serializer.validated_data.get('extract_keywords', True),
extract_entities=serializer.validated_data.get('extract_entities', True),
detect_emotions=serializer.validated_data.get('detect_emotions', True),
)
response_serializer = AnalyzeTextResponseSerializer(result)
return Response(response_serializer.data)
@extend_schema(
summary="Analyze batch of texts",
description="Analyze multiple texts in a single request",
request=BatchAnalyzeRequestSerializer,
responses={200: BatchAnalyzeResponseSerializer}
)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def analyze_batch(request):
"""
Analyze multiple texts in batch.
POST /api/ai-engine/analyze-batch/
Request body:
{
"texts": [
"The service was excellent!",
"Very disappointed with the wait time.",
"Average experience overall."
],
"language": "en" // optional
}
Response:
{
"results": [
{
"text": "The service was excellent!",
"sentiment": "positive",
...
},
...
],
"total": 3,
"processing_time_ms": 15
}
"""
serializer = BatchAnalyzeRequestSerializer(data=request.data)
if not serializer.is_valid():
return Response(
serializer.errors,
status=status.HTTP_400_BAD_REQUEST
)
start_time = time.time()
# Perform batch analysis
results = AIEngineService.sentiment.analyze_batch(
texts=serializer.validated_data['texts'],
language=serializer.validated_data.get('language'),
)
processing_time_ms = int((time.time() - start_time) * 1000)
response_data = {
'results': results,
'total': len(results),
'processing_time_ms': processing_time_ms,
}
response_serializer = BatchAnalyzeResponseSerializer(response_data)
return Response(response_serializer.data)
@extend_schema(
summary="Get sentiment for object",
description="Get sentiment analysis result for a specific object",
responses={200: SentimentResultSerializer}
)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_sentiment_for_object(request, content_type_id, object_id):
"""
Get sentiment result for a specific object.
GET /api/ai-engine/sentiment/{content_type_id}/{object_id}/
"""
from django.contrib.contenttypes.models import ContentType
try:
content_type = ContentType.objects.get(id=content_type_id)
except ContentType.DoesNotExist:
return Response(
{'error': 'Content type not found'},
status=status.HTTP_404_NOT_FOUND
)
sentiment_result = SentimentResult.objects.filter(
content_type=content_type,
object_id=object_id
).first()
if not sentiment_result:
return Response(
{'error': 'Sentiment result not found'},
status=status.HTTP_404_NOT_FOUND
)
serializer = SentimentResultSerializer(sentiment_result)
return Response(serializer.data)