396 lines
16 KiB
Python
396 lines
16 KiB
Python
"""
|
|
Django REST Framework API views for psychology app.
|
|
"""
|
|
|
|
from rest_framework import viewsets, filters, status
|
|
from rest_framework.decorators import action
|
|
from rest_framework.response import Response
|
|
from rest_framework.permissions import IsAuthenticated
|
|
from django_filters.rest_framework import DjangoFilterBackend
|
|
from django.db.models import Q, Count, Avg
|
|
from django.utils import timezone
|
|
from datetime import timedelta
|
|
|
|
from .models import (
|
|
PsychologyConsultation,
|
|
PsychologyAssessment,
|
|
PsychologySession,
|
|
PsychologyGoal,
|
|
PsychologyProgressReport,
|
|
)
|
|
from .serializers import (
|
|
PsychologyConsultationSerializer,
|
|
PsychologyAssessmentSerializer,
|
|
PsychologySessionSerializer,
|
|
PsychologyGoalSerializer,
|
|
PsychologyProgressReportSerializer,
|
|
)
|
|
|
|
|
|
class PsychologyConsultationViewSet(viewsets.ModelViewSet):
|
|
"""
|
|
API endpoint for psychology consultations.
|
|
|
|
Provides CRUD operations and additional actions for consultations.
|
|
"""
|
|
queryset = PsychologyConsultation.objects.all()
|
|
serializer_class = PsychologyConsultationSerializer
|
|
permission_classes = [IsAuthenticated]
|
|
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
|
|
filterset_fields = ['patient', 'provider', 'referral_reason', 'suicide_risk', 'homicide_risk', 'tenant']
|
|
search_fields = ['patient__first_name_en', 'patient__last_name_en', 'patient__mrn',
|
|
'presenting_problem', 'clinical_impressions', 'provisional_diagnosis']
|
|
ordering_fields = ['consultation_date', 'created_at']
|
|
ordering = ['-consultation_date']
|
|
|
|
def get_queryset(self):
|
|
"""Filter queryset based on user permissions."""
|
|
queryset = super().get_queryset()
|
|
return queryset.select_related('patient', 'provider', 'tenant', 'appointment')
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def high_risk(self, request):
|
|
"""Get consultations with high suicide or homicide risk."""
|
|
high_risk_consultations = self.get_queryset().filter(
|
|
Q(suicide_risk__in=['MODERATE', 'HIGH']) |
|
|
Q(homicide_risk__in=['MODERATE', 'HIGH'])
|
|
)
|
|
serializer = self.get_serializer(high_risk_consultations, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def recent(self, request):
|
|
"""Get consultations from the last 30 days."""
|
|
thirty_days_ago = timezone.now().date() - timedelta(days=30)
|
|
recent_consultations = self.get_queryset().filter(
|
|
consultation_date__gte=thirty_days_ago
|
|
)
|
|
serializer = self.get_serializer(recent_consultations, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def statistics(self, request):
|
|
"""Get consultation statistics."""
|
|
queryset = self.get_queryset()
|
|
|
|
stats = {
|
|
'total_consultations': queryset.count(),
|
|
'by_referral_reason': dict(
|
|
queryset.values('referral_reason').annotate(count=Count('id')).values_list('referral_reason', 'count')
|
|
),
|
|
'high_risk_count': queryset.filter(
|
|
Q(suicide_risk__in=['MODERATE', 'HIGH']) |
|
|
Q(homicide_risk__in=['MODERATE', 'HIGH'])
|
|
).count(),
|
|
'signed_count': queryset.filter(signed_by__isnull=False).count(),
|
|
'unsigned_count': queryset.filter(signed_by__isnull=True).count(),
|
|
}
|
|
|
|
return Response(stats)
|
|
|
|
@action(detail=True, methods=['post'])
|
|
def sign(self, request, pk=None):
|
|
"""Sign a consultation."""
|
|
consultation = self.get_object()
|
|
if consultation.signed_by:
|
|
return Response(
|
|
{'error': 'Consultation already signed'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
consultation.signed_by = request.user
|
|
consultation.signed_at = timezone.now()
|
|
consultation.save()
|
|
|
|
serializer = self.get_serializer(consultation)
|
|
return Response(serializer.data)
|
|
|
|
|
|
class PsychologyAssessmentViewSet(viewsets.ModelViewSet):
|
|
"""
|
|
API endpoint for psychology assessments.
|
|
|
|
Provides CRUD operations and additional actions for assessments.
|
|
"""
|
|
queryset = PsychologyAssessment.objects.all()
|
|
serializer_class = PsychologyAssessmentSerializer
|
|
permission_classes = [IsAuthenticated]
|
|
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
|
|
filterset_fields = ['patient', 'provider', 'assessment_type', 'tenant']
|
|
search_fields = ['patient__first_name_en', 'patient__last_name_en', 'patient__mrn',
|
|
'reason_for_assessment', 'diagnostic_impressions', 'dsm5_diagnosis']
|
|
ordering_fields = ['assessment_date', 'created_at']
|
|
ordering = ['-assessment_date']
|
|
|
|
def get_queryset(self):
|
|
"""Filter queryset based on user permissions."""
|
|
queryset = super().get_queryset()
|
|
return queryset.select_related('patient', 'provider', 'tenant', 'appointment')
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def by_type(self, request):
|
|
"""Get assessments grouped by type."""
|
|
assessment_type = request.query_params.get('type')
|
|
if assessment_type:
|
|
assessments = self.get_queryset().filter(assessment_type=assessment_type)
|
|
else:
|
|
assessments = self.get_queryset()
|
|
|
|
serializer = self.get_serializer(assessments, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def statistics(self, request):
|
|
"""Get assessment statistics."""
|
|
queryset = self.get_queryset()
|
|
|
|
stats = {
|
|
'total_assessments': queryset.count(),
|
|
'by_type': dict(
|
|
queryset.values('assessment_type').annotate(count=Count('id')).values_list('assessment_type', 'count')
|
|
),
|
|
'signed_count': queryset.filter(signed_by__isnull=False).count(),
|
|
'unsigned_count': queryset.filter(signed_by__isnull=True).count(),
|
|
}
|
|
|
|
return Response(stats)
|
|
|
|
|
|
class PsychologySessionViewSet(viewsets.ModelViewSet):
|
|
"""
|
|
API endpoint for psychology sessions.
|
|
|
|
Provides CRUD operations and additional actions for sessions.
|
|
"""
|
|
queryset = PsychologySession.objects.all()
|
|
serializer_class = PsychologySessionSerializer
|
|
permission_classes = [IsAuthenticated]
|
|
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
|
|
filterset_fields = ['patient', 'provider', 'session_type', 'therapy_modality', 'current_risk_level', 'tenant']
|
|
search_fields = ['patient__first_name_en', 'patient__last_name_en', 'patient__mrn',
|
|
'presenting_issues', 'interventions_used', 'clinical_notes']
|
|
ordering_fields = ['session_date', 'session_number', 'created_at']
|
|
ordering = ['-session_date', '-session_number']
|
|
|
|
def get_queryset(self):
|
|
"""Filter queryset based on user permissions."""
|
|
queryset = super().get_queryset()
|
|
return queryset.select_related('patient', 'provider', 'tenant', 'appointment')
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def by_patient(self, request):
|
|
"""Get all sessions for a specific patient."""
|
|
patient_id = request.query_params.get('patient_id')
|
|
if not patient_id:
|
|
return Response(
|
|
{'error': 'patient_id parameter required'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
sessions = self.get_queryset().filter(patient_id=patient_id)
|
|
serializer = self.get_serializer(sessions, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def high_risk(self, request):
|
|
"""Get sessions with high risk levels."""
|
|
high_risk_sessions = self.get_queryset().filter(
|
|
current_risk_level__in=['MODERATE', 'HIGH']
|
|
)
|
|
serializer = self.get_serializer(high_risk_sessions, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def statistics(self, request):
|
|
"""Get session statistics."""
|
|
queryset = self.get_queryset()
|
|
|
|
stats = {
|
|
'total_sessions': queryset.count(),
|
|
'by_type': dict(
|
|
queryset.values('session_type').annotate(count=Count('id')).values_list('session_type', 'count')
|
|
),
|
|
'by_modality': dict(
|
|
queryset.values('therapy_modality').annotate(count=Count('id')).values_list('therapy_modality', 'count')
|
|
),
|
|
'average_duration': queryset.aggregate(avg_duration=Avg('duration_minutes'))['avg_duration'],
|
|
'high_risk_count': queryset.filter(current_risk_level__in=['MODERATE', 'HIGH']).count(),
|
|
'signed_count': queryset.filter(signed_by__isnull=False).count(),
|
|
'unsigned_count': queryset.filter(signed_by__isnull=True).count(),
|
|
}
|
|
|
|
return Response(stats)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def recent(self, request):
|
|
"""Get sessions from the last 30 days."""
|
|
thirty_days_ago = timezone.now().date() - timedelta(days=30)
|
|
recent_sessions = self.get_queryset().filter(
|
|
session_date__gte=thirty_days_ago
|
|
)
|
|
serializer = self.get_serializer(recent_sessions, many=True)
|
|
return Response(serializer.data)
|
|
|
|
|
|
class PsychologyGoalViewSet(viewsets.ModelViewSet):
|
|
"""
|
|
API endpoint for psychology goals.
|
|
|
|
Provides CRUD operations and additional actions for goals.
|
|
"""
|
|
queryset = PsychologyGoal.objects.all()
|
|
serializer_class = PsychologyGoalSerializer
|
|
permission_classes = [IsAuthenticated]
|
|
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
|
|
filterset_fields = ['patient', 'consultation', 'status']
|
|
search_fields = ['patient__first_name_en', 'patient__last_name_en', 'patient__mrn',
|
|
'goal_description', 'progress_notes']
|
|
ordering_fields = ['target_date', 'progress_percentage', 'created_at']
|
|
ordering = ['target_date']
|
|
|
|
def get_queryset(self):
|
|
"""Filter queryset based on user permissions."""
|
|
queryset = super().get_queryset()
|
|
return queryset.select_related('patient', 'consultation')
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def active(self, request):
|
|
"""Get active goals (in progress)."""
|
|
active_goals = self.get_queryset().filter(status='IN_PROGRESS')
|
|
serializer = self.get_serializer(active_goals, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def achieved(self, request):
|
|
"""Get achieved goals."""
|
|
achieved_goals = self.get_queryset().filter(status='ACHIEVED')
|
|
serializer = self.get_serializer(achieved_goals, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def overdue(self, request):
|
|
"""Get overdue goals."""
|
|
today = timezone.now().date()
|
|
overdue_goals = self.get_queryset().filter(
|
|
target_date__lt=today,
|
|
status__in=['NOT_STARTED', 'IN_PROGRESS']
|
|
)
|
|
serializer = self.get_serializer(overdue_goals, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def statistics(self, request):
|
|
"""Get goal statistics."""
|
|
queryset = self.get_queryset()
|
|
|
|
stats = {
|
|
'total_goals': queryset.count(),
|
|
'by_status': dict(
|
|
queryset.values('status').annotate(count=Count('id')).values_list('status', 'count')
|
|
),
|
|
'average_progress': queryset.aggregate(avg_progress=Avg('progress_percentage'))['avg_progress'],
|
|
'achieved_count': queryset.filter(status='ACHIEVED').count(),
|
|
'in_progress_count': queryset.filter(status='IN_PROGRESS').count(),
|
|
'overdue_count': queryset.filter(
|
|
target_date__lt=timezone.now().date(),
|
|
status__in=['NOT_STARTED', 'IN_PROGRESS']
|
|
).count(),
|
|
}
|
|
|
|
return Response(stats)
|
|
|
|
@action(detail=True, methods=['post'])
|
|
def update_progress(self, request, pk=None):
|
|
"""Update goal progress percentage."""
|
|
goal = self.get_object()
|
|
progress = request.data.get('progress_percentage')
|
|
|
|
if progress is None:
|
|
return Response(
|
|
{'error': 'progress_percentage required'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
try:
|
|
progress = int(progress)
|
|
if not 0 <= progress <= 100:
|
|
raise ValueError
|
|
except (ValueError, TypeError):
|
|
return Response(
|
|
{'error': 'progress_percentage must be between 0 and 100'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
goal.progress_percentage = progress
|
|
|
|
# Auto-update status based on progress
|
|
if progress == 0:
|
|
goal.status = 'NOT_STARTED'
|
|
elif progress == 100:
|
|
goal.status = 'ACHIEVED'
|
|
goal.achieved_date = timezone.now().date()
|
|
else:
|
|
goal.status = 'IN_PROGRESS'
|
|
|
|
goal.save()
|
|
|
|
serializer = self.get_serializer(goal)
|
|
return Response(serializer.data)
|
|
|
|
|
|
class PsychologyProgressReportViewSet(viewsets.ModelViewSet):
|
|
"""
|
|
API endpoint for psychology progress reports.
|
|
|
|
Provides CRUD operations and additional actions for progress reports.
|
|
"""
|
|
queryset = PsychologyProgressReport.objects.all()
|
|
serializer_class = PsychologyProgressReportSerializer
|
|
permission_classes = [IsAuthenticated]
|
|
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
|
|
filterset_fields = ['patient', 'provider', 'continue_treatment', 'tenant']
|
|
search_fields = ['patient__first_name_en', 'patient__last_name_en', 'patient__mrn',
|
|
'presenting_problems_summary', 'overall_progress', 'recommendations']
|
|
ordering_fields = ['report_date', 'created_at']
|
|
ordering = ['-report_date']
|
|
|
|
def get_queryset(self):
|
|
"""Filter queryset based on user permissions."""
|
|
queryset = super().get_queryset()
|
|
return queryset.select_related('patient', 'provider', 'tenant')
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def recent(self, request):
|
|
"""Get reports from the last 90 days."""
|
|
ninety_days_ago = timezone.now().date() - timedelta(days=90)
|
|
recent_reports = self.get_queryset().filter(
|
|
report_date__gte=ninety_days_ago
|
|
)
|
|
serializer = self.get_serializer(recent_reports, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def discharge_ready(self, request):
|
|
"""Get reports recommending discharge."""
|
|
discharge_reports = self.get_queryset().filter(continue_treatment=False)
|
|
serializer = self.get_serializer(discharge_reports, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def statistics(self, request):
|
|
"""Get progress report statistics."""
|
|
queryset = self.get_queryset()
|
|
|
|
stats = {
|
|
'total_reports': queryset.count(),
|
|
'continue_treatment_count': queryset.filter(continue_treatment=True).count(),
|
|
'discharge_recommended_count': queryset.filter(continue_treatment=False).count(),
|
|
'average_attendance_rate': queryset.aggregate(
|
|
avg_attendance=Avg('sessions_attended') / Avg('sessions_scheduled') * 100
|
|
),
|
|
'signed_count': queryset.filter(signed_by__isnull=False).count(),
|
|
'unsigned_count': queryset.filter(signed_by__isnull=True).count(),
|
|
}
|
|
|
|
return Response(stats)
|