421 lines
14 KiB
Python
421 lines
14 KiB
Python
"""
|
|
Physicians Console UI views - Server-rendered templates for physician management
|
|
"""
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.core.paginator import Paginator
|
|
from django.db.models import Avg, Count, Q
|
|
from django.shortcuts import get_object_or_404, render
|
|
from django.utils import timezone
|
|
|
|
from apps.organizations.models import Department, Hospital, Physician
|
|
|
|
from .models import PhysicianMonthlyRating
|
|
|
|
|
|
@login_required
|
|
def physician_list(request):
|
|
"""
|
|
Physicians list view with filters.
|
|
|
|
Features:
|
|
- Server-side pagination
|
|
- Filters (hospital, department, specialization, status)
|
|
- Search by name or license number
|
|
- Current month rating display
|
|
"""
|
|
# Base queryset with optimizations
|
|
queryset = Physician.objects.select_related('hospital', 'department')
|
|
|
|
# Apply RBAC filters
|
|
user = request.user
|
|
if user.is_px_admin():
|
|
pass # See all
|
|
elif user.hospital:
|
|
queryset = queryset.filter(hospital=user.hospital)
|
|
else:
|
|
queryset = queryset.none()
|
|
|
|
# Apply filters
|
|
hospital_filter = request.GET.get('hospital')
|
|
if hospital_filter:
|
|
queryset = queryset.filter(hospital_id=hospital_filter)
|
|
|
|
department_filter = request.GET.get('department')
|
|
if department_filter:
|
|
queryset = queryset.filter(department_id=department_filter)
|
|
|
|
specialization_filter = request.GET.get('specialization')
|
|
if specialization_filter:
|
|
queryset = queryset.filter(specialization__icontains=specialization_filter)
|
|
|
|
status_filter = request.GET.get('status', 'active')
|
|
if status_filter:
|
|
queryset = queryset.filter(status=status_filter)
|
|
|
|
# Search
|
|
search_query = request.GET.get('search')
|
|
if search_query:
|
|
queryset = queryset.filter(
|
|
Q(first_name__icontains=search_query) |
|
|
Q(last_name__icontains=search_query) |
|
|
Q(license_number__icontains=search_query) |
|
|
Q(specialization__icontains=search_query)
|
|
)
|
|
|
|
# Ordering
|
|
order_by = request.GET.get('order_by', 'last_name')
|
|
queryset = queryset.order_by(order_by)
|
|
|
|
# Pagination
|
|
page_size = int(request.GET.get('page_size', 25))
|
|
paginator = Paginator(queryset, page_size)
|
|
page_number = request.GET.get('page', 1)
|
|
page_obj = paginator.get_page(page_number)
|
|
|
|
# Get current month ratings for displayed physicians
|
|
now = timezone.now()
|
|
physician_ids = [p.id for p in page_obj.object_list]
|
|
current_ratings = PhysicianMonthlyRating.objects.filter(
|
|
physician_id__in=physician_ids,
|
|
year=now.year,
|
|
month=now.month
|
|
).select_related('physician')
|
|
|
|
# Create rating lookup
|
|
ratings_dict = {r.physician_id: r for r in current_ratings}
|
|
|
|
# Attach ratings to physicians
|
|
for physician in page_obj.object_list:
|
|
physician.current_rating = ratings_dict.get(physician.id)
|
|
|
|
# Get filter options
|
|
hospitals = Hospital.objects.filter(status='active')
|
|
if not user.is_px_admin() and user.hospital:
|
|
hospitals = hospitals.filter(id=user.hospital.id)
|
|
|
|
departments = Department.objects.filter(status='active')
|
|
if not user.is_px_admin() and user.hospital:
|
|
departments = departments.filter(hospital=user.hospital)
|
|
|
|
# Get unique specializations
|
|
specializations = Physician.objects.values_list('specialization', flat=True).distinct().order_by('specialization')
|
|
|
|
# Statistics
|
|
stats = {
|
|
'total': queryset.count(),
|
|
'active': queryset.filter(status='active').count(),
|
|
}
|
|
|
|
context = {
|
|
'page_obj': page_obj,
|
|
'physicians': page_obj.object_list,
|
|
'stats': stats,
|
|
'hospitals': hospitals,
|
|
'departments': departments,
|
|
'specializations': specializations,
|
|
'filters': request.GET,
|
|
'current_year': now.year,
|
|
'current_month': now.month,
|
|
}
|
|
|
|
return render(request, 'physicians/physician_list.html', context)
|
|
|
|
|
|
@login_required
|
|
def physician_detail(request, pk):
|
|
"""
|
|
Physician detail view with performance metrics.
|
|
|
|
Features:
|
|
- Full physician details
|
|
- Current month rating
|
|
- Year-to-date performance
|
|
- Monthly ratings history (last 12 months)
|
|
- Performance trends
|
|
"""
|
|
physician = get_object_or_404(
|
|
Physician.objects.select_related('hospital', 'department'),
|
|
pk=pk
|
|
)
|
|
|
|
# Check permission
|
|
user = request.user
|
|
if not user.is_px_admin() and user.hospital:
|
|
if physician.hospital != user.hospital:
|
|
from django.http import Http404
|
|
raise Http404("Physician not found")
|
|
|
|
now = timezone.now()
|
|
current_year = now.year
|
|
current_month = now.month
|
|
|
|
# Get current month rating
|
|
current_month_rating = PhysicianMonthlyRating.objects.filter(
|
|
physician=physician,
|
|
year=current_year,
|
|
month=current_month
|
|
).first()
|
|
|
|
# Get previous month rating
|
|
prev_month = current_month - 1 if current_month > 1 else 12
|
|
prev_year = current_year if current_month > 1 else current_year - 1
|
|
previous_month_rating = PhysicianMonthlyRating.objects.filter(
|
|
physician=physician,
|
|
year=prev_year,
|
|
month=prev_month
|
|
).first()
|
|
|
|
# Get year-to-date stats
|
|
ytd_ratings = PhysicianMonthlyRating.objects.filter(
|
|
physician=physician,
|
|
year=current_year
|
|
)
|
|
|
|
ytd_stats = ytd_ratings.aggregate(
|
|
avg_rating=Avg('average_rating'),
|
|
total_surveys=Count('id')
|
|
)
|
|
|
|
# Get last 12 months ratings
|
|
ratings_history = PhysicianMonthlyRating.objects.filter(
|
|
physician=physician
|
|
).order_by('-year', '-month')[:12]
|
|
|
|
# Get best and worst months from all ratings (not just last 12 months)
|
|
all_ratings = PhysicianMonthlyRating.objects.filter(physician=physician)
|
|
best_month = all_ratings.order_by('-average_rating').first()
|
|
worst_month = all_ratings.order_by('average_rating').first()
|
|
|
|
# Determine trend
|
|
trend = 'stable'
|
|
trend_percentage = 0
|
|
if current_month_rating and previous_month_rating:
|
|
diff = float(current_month_rating.average_rating - previous_month_rating.average_rating)
|
|
if previous_month_rating.average_rating > 0:
|
|
trend_percentage = (diff / float(previous_month_rating.average_rating)) * 100
|
|
|
|
if diff > 0.1:
|
|
trend = 'improving'
|
|
elif diff < -0.1:
|
|
trend = 'declining'
|
|
|
|
context = {
|
|
'physician': physician,
|
|
'current_month_rating': current_month_rating,
|
|
'previous_month_rating': previous_month_rating,
|
|
'ytd_average': ytd_stats['avg_rating'],
|
|
'ytd_surveys': ytd_stats['total_surveys'],
|
|
'ratings_history': ratings_history,
|
|
'best_month': best_month,
|
|
'worst_month': worst_month,
|
|
'trend': trend,
|
|
'trend_percentage': abs(trend_percentage),
|
|
'current_year': current_year,
|
|
'current_month': current_month,
|
|
}
|
|
|
|
return render(request, 'physicians/physician_detail.html', context)
|
|
|
|
|
|
@login_required
|
|
def leaderboard(request):
|
|
"""
|
|
Physician leaderboard view.
|
|
|
|
Features:
|
|
- Top-rated physicians for selected period
|
|
- Filters (hospital, department, month/year)
|
|
- Ranking with trends
|
|
- Performance distribution
|
|
"""
|
|
# Get parameters
|
|
now = timezone.now()
|
|
year = int(request.GET.get('year', now.year))
|
|
month = int(request.GET.get('month', now.month))
|
|
hospital_filter = request.GET.get('hospital')
|
|
department_filter = request.GET.get('department')
|
|
limit = int(request.GET.get('limit', 20))
|
|
|
|
# Build queryset
|
|
queryset = PhysicianMonthlyRating.objects.filter(
|
|
year=year,
|
|
month=month
|
|
).select_related('physician', 'physician__hospital', 'physician__department')
|
|
|
|
# Apply RBAC filters
|
|
user = request.user
|
|
if not user.is_px_admin() and user.hospital:
|
|
queryset = queryset.filter(physician__hospital=user.hospital)
|
|
|
|
# Apply filters
|
|
if hospital_filter:
|
|
queryset = queryset.filter(physician__hospital_id=hospital_filter)
|
|
|
|
if department_filter:
|
|
queryset = queryset.filter(physician__department_id=department_filter)
|
|
|
|
# Order by rating
|
|
queryset = queryset.order_by('-average_rating')[:limit]
|
|
|
|
# Get previous month for trend
|
|
prev_month = month - 1 if month > 1 else 12
|
|
prev_year = year if month > 1 else year - 1
|
|
|
|
# Build leaderboard with trends
|
|
leaderboard = []
|
|
for rank, rating in enumerate(queryset, start=1):
|
|
# Get previous month rating for trend
|
|
prev_rating = PhysicianMonthlyRating.objects.filter(
|
|
physician=rating.physician,
|
|
year=prev_year,
|
|
month=prev_month
|
|
).first()
|
|
|
|
trend = 'stable'
|
|
trend_value = 0
|
|
if prev_rating:
|
|
diff = float(rating.average_rating - prev_rating.average_rating)
|
|
trend_value = diff
|
|
if diff > 0.1:
|
|
trend = 'up'
|
|
elif diff < -0.1:
|
|
trend = 'down'
|
|
|
|
leaderboard.append({
|
|
'rank': rank,
|
|
'rating': rating,
|
|
'physician': rating.physician,
|
|
'trend': trend,
|
|
'trend_value': trend_value,
|
|
'prev_rating': prev_rating
|
|
})
|
|
|
|
# Get filter options
|
|
hospitals = Hospital.objects.filter(status='active')
|
|
if not user.is_px_admin() and user.hospital:
|
|
hospitals = hospitals.filter(id=user.hospital.id)
|
|
|
|
departments = Department.objects.filter(status='active')
|
|
if not user.is_px_admin() and user.hospital:
|
|
departments = departments.filter(hospital=user.hospital)
|
|
|
|
# Calculate statistics
|
|
all_ratings = PhysicianMonthlyRating.objects.filter(year=year, month=month)
|
|
if not user.is_px_admin() and user.hospital:
|
|
all_ratings = all_ratings.filter(physician__hospital=user.hospital)
|
|
|
|
stats = all_ratings.aggregate(
|
|
total_physicians=Count('id'),
|
|
average_rating=Avg('average_rating'),
|
|
total_surveys=Count('total_surveys')
|
|
)
|
|
|
|
# Distribution
|
|
excellent = all_ratings.filter(average_rating__gte=4.5).count()
|
|
good = all_ratings.filter(average_rating__gte=3.5, average_rating__lt=4.5).count()
|
|
average = all_ratings.filter(average_rating__gte=2.5, average_rating__lt=3.5).count()
|
|
poor = all_ratings.filter(average_rating__lt=2.5).count()
|
|
|
|
context = {
|
|
'leaderboard': leaderboard,
|
|
'year': year,
|
|
'month': month,
|
|
'hospitals': hospitals,
|
|
'departments': departments,
|
|
'filters': request.GET,
|
|
'stats': stats,
|
|
'distribution': {
|
|
'excellent': excellent,
|
|
'good': good,
|
|
'average': average,
|
|
'poor': poor
|
|
}
|
|
}
|
|
|
|
return render(request, 'physicians/leaderboard.html', context)
|
|
|
|
|
|
@login_required
|
|
def ratings_list(request):
|
|
"""
|
|
Monthly ratings list view with filters.
|
|
|
|
Features:
|
|
- All monthly ratings
|
|
- Filters (physician, hospital, department, year, month)
|
|
- Search by physician name
|
|
- Pagination
|
|
"""
|
|
# Base queryset
|
|
queryset = PhysicianMonthlyRating.objects.select_related(
|
|
'physician', 'physician__hospital', 'physician__department'
|
|
)
|
|
|
|
# Apply RBAC filters
|
|
user = request.user
|
|
if not user.is_px_admin() and user.hospital:
|
|
queryset = queryset.filter(physician__hospital=user.hospital)
|
|
|
|
# Apply filters
|
|
physician_filter = request.GET.get('physician')
|
|
if physician_filter:
|
|
queryset = queryset.filter(physician_id=physician_filter)
|
|
|
|
hospital_filter = request.GET.get('hospital')
|
|
if hospital_filter:
|
|
queryset = queryset.filter(physician__hospital_id=hospital_filter)
|
|
|
|
department_filter = request.GET.get('department')
|
|
if department_filter:
|
|
queryset = queryset.filter(physician__department_id=department_filter)
|
|
|
|
year_filter = request.GET.get('year')
|
|
if year_filter:
|
|
queryset = queryset.filter(year=int(year_filter))
|
|
|
|
month_filter = request.GET.get('month')
|
|
if month_filter:
|
|
queryset = queryset.filter(month=int(month_filter))
|
|
|
|
# Search
|
|
search_query = request.GET.get('search')
|
|
if search_query:
|
|
queryset = queryset.filter(
|
|
Q(physician__first_name__icontains=search_query) |
|
|
Q(physician__last_name__icontains=search_query) |
|
|
Q(physician__license_number__icontains=search_query)
|
|
)
|
|
|
|
# Ordering
|
|
order_by = request.GET.get('order_by', '-year,-month,-average_rating')
|
|
queryset = queryset.order_by(*order_by.split(','))
|
|
|
|
# Pagination
|
|
page_size = int(request.GET.get('page_size', 25))
|
|
paginator = Paginator(queryset, page_size)
|
|
page_number = request.GET.get('page', 1)
|
|
page_obj = paginator.get_page(page_number)
|
|
|
|
# Get filter options
|
|
hospitals = Hospital.objects.filter(status='active')
|
|
if not user.is_px_admin() and user.hospital:
|
|
hospitals = hospitals.filter(id=user.hospital.id)
|
|
|
|
departments = Department.objects.filter(status='active')
|
|
if not user.is_px_admin() and user.hospital:
|
|
departments = departments.filter(hospital=user.hospital)
|
|
|
|
# Get available years
|
|
years = PhysicianMonthlyRating.objects.values_list('year', flat=True).distinct().order_by('-year')
|
|
|
|
context = {
|
|
'page_obj': page_obj,
|
|
'ratings': page_obj.object_list,
|
|
'hospitals': hospitals,
|
|
'departments': departments,
|
|
'years': years,
|
|
'filters': request.GET,
|
|
}
|
|
|
|
return render(request, 'physicians/ratings_list.html', context)
|