570 lines
18 KiB
Python
570 lines
18 KiB
Python
"""
|
|
PX Sources UI views - HTML template rendering
|
|
"""
|
|
from django.contrib import messages
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.db import models
|
|
from django.http import JsonResponse
|
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from .models import PXSource, SourceUser
|
|
from apps.accounts.models import User
|
|
|
|
|
|
@login_required
|
|
def source_list(request):
|
|
"""
|
|
List all PX sources
|
|
"""
|
|
sources = PXSource.objects.all()
|
|
|
|
# Filter by active status
|
|
is_active = request.GET.get('is_active')
|
|
if is_active:
|
|
sources = sources.filter(is_active=is_active == 'true')
|
|
|
|
# Search
|
|
search = request.GET.get('search')
|
|
if search:
|
|
sources = sources.filter(
|
|
models.Q(name_en__icontains=search) |
|
|
models.Q(name_ar__icontains=search) |
|
|
models.Q(description__icontains=search)
|
|
)
|
|
|
|
sources = sources.order_by('name_en')
|
|
|
|
context = {
|
|
'sources': sources,
|
|
'is_active': is_active,
|
|
'search': search,
|
|
}
|
|
|
|
return render(request, 'px_sources/source_list.html', context)
|
|
|
|
|
|
@login_required
|
|
def source_detail(request, pk):
|
|
"""
|
|
View source details
|
|
"""
|
|
source = get_object_or_404(PXSource, pk=pk)
|
|
usage_records = source.usage_records.select_related(
|
|
'content_type', 'hospital', 'user'
|
|
).order_by('-created_at')[:20]
|
|
|
|
# Get source users for this source
|
|
source_users = source.source_users.select_related('user').order_by('-created_at')
|
|
|
|
# Get available users (not already assigned to this source)
|
|
assigned_user_ids = source_users.values_list('user_id', flat=True)
|
|
available_users = User.objects.exclude(id__in=assigned_user_ids).order_by('email')
|
|
|
|
context = {
|
|
'source': source,
|
|
'usage_records': usage_records,
|
|
'source_users': source_users,
|
|
'available_users': available_users,
|
|
}
|
|
|
|
return render(request, 'px_sources/source_detail.html', context)
|
|
|
|
|
|
@login_required
|
|
def source_create(request):
|
|
"""
|
|
Create a new PX source
|
|
"""
|
|
# if not (request.user.is_px_admin() or request.user.is_hospital_admin()):
|
|
# messages.error(request, _("You don't have permission to create sources."))
|
|
# return redirect('px_sources:source_list')
|
|
|
|
if request.method == 'POST':
|
|
try:
|
|
source = PXSource(
|
|
name_en=request.POST.get('name_en'),
|
|
name_ar=request.POST.get('name_ar', ''),
|
|
description=request.POST.get('description', ''),
|
|
is_active=request.POST.get('is_active') == 'on',
|
|
)
|
|
source.save()
|
|
|
|
messages.success(request, _("Source created successfully!"))
|
|
return redirect('px_sources:source_detail', pk=source.pk)
|
|
|
|
except Exception as e:
|
|
messages.error(request, _("Error creating source: {}").format(str(e)))
|
|
|
|
context = {}
|
|
|
|
return render(request, 'px_sources/source_form.html', context)
|
|
|
|
|
|
@login_required
|
|
def source_edit(request, pk):
|
|
"""
|
|
Edit an existing PX source
|
|
"""
|
|
if not (request.user.is_px_admin() or request.user.is_hospital_admin()):
|
|
messages.error(request, _("You don't have permission to edit sources."))
|
|
return redirect('px_sources:source_detail', pk=pk)
|
|
|
|
source = get_object_or_404(PXSource, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
try:
|
|
source.name_en = request.POST.get('name_en')
|
|
source.name_ar = request.POST.get('name_ar', '')
|
|
source.description = request.POST.get('description', '')
|
|
source.is_active = request.POST.get('is_active') == 'on'
|
|
source.save()
|
|
|
|
messages.success(request, _("Source updated successfully!"))
|
|
return redirect('px_sources:source_detail', pk=source.pk)
|
|
|
|
except Exception as e:
|
|
messages.error(request, _("Error updating source: {}").format(str(e)))
|
|
|
|
context = {
|
|
'source': source,
|
|
}
|
|
|
|
return render(request, 'px_sources/source_form.html', context)
|
|
|
|
|
|
@login_required
|
|
def source_delete(request, pk):
|
|
"""
|
|
Delete a PX source
|
|
"""
|
|
if not request.user.is_px_admin():
|
|
messages.error(request, _("You don't have permission to delete sources."))
|
|
return redirect('px_sources:source_detail', pk=pk)
|
|
|
|
source = get_object_or_404(PXSource, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
source_name = source.name_en
|
|
source.delete()
|
|
messages.success(request, _("Source '{}' deleted successfully!").format(source_name))
|
|
return redirect('px_sources:source_list')
|
|
|
|
context = {
|
|
'source': source,
|
|
}
|
|
|
|
return render(request, 'px_sources/source_confirm_delete.html', context)
|
|
|
|
|
|
@login_required
|
|
def source_toggle_status(request, pk):
|
|
"""
|
|
Toggle source active status (AJAX)
|
|
"""
|
|
if not (request.user.is_px_admin() or request.user.is_hospital_admin()):
|
|
return JsonResponse({'error': 'Permission denied'}, status=403)
|
|
|
|
if request.method != 'POST':
|
|
return JsonResponse({'error': 'Method not allowed'}, status=405)
|
|
|
|
source = get_object_or_404(PXSource, pk=pk)
|
|
source.is_active = not source.is_active
|
|
source.save()
|
|
|
|
return JsonResponse({
|
|
'success': True,
|
|
'is_active': source.is_active,
|
|
'message': 'Source {} successfully'.format(
|
|
'activated' if source.is_active else 'deactivated'
|
|
)
|
|
})
|
|
|
|
|
|
@login_required
|
|
def ajax_search_sources(request):
|
|
"""
|
|
AJAX endpoint for searching sources
|
|
"""
|
|
term = request.GET.get('term', '')
|
|
|
|
queryset = PXSource.objects.filter(is_active=True)
|
|
|
|
if term:
|
|
queryset = queryset.filter(
|
|
models.Q(name_en__icontains=term) |
|
|
models.Q(name_ar__icontains=term) |
|
|
models.Q(description__icontains=term)
|
|
)
|
|
|
|
sources = queryset.order_by('name_en')[:20]
|
|
|
|
results = [
|
|
{
|
|
'id': str(source.id),
|
|
'text': source.name_en,
|
|
'name_en': source.name_en,
|
|
'name_ar': source.name_ar,
|
|
}
|
|
for source in sources
|
|
]
|
|
|
|
return JsonResponse({'results': results})
|
|
|
|
|
|
@login_required
|
|
def source_user_dashboard(request):
|
|
"""
|
|
Dashboard for source users.
|
|
|
|
Shows:
|
|
- User's assigned source
|
|
- Statistics (complaints, inquiries from their source)
|
|
- Create buttons for complaints/inquiries
|
|
- Tables of recent complaints/inquiries from their source
|
|
"""
|
|
# Get source user profile
|
|
source_user = SourceUser.get_active_source_user(request.user)
|
|
|
|
if not source_user:
|
|
messages.error(
|
|
request,
|
|
_("You are not assigned as a source user. Please contact your administrator.")
|
|
)
|
|
return redirect('/')
|
|
|
|
# Get source
|
|
source = source_user.source
|
|
|
|
# Get complaints from this source (recent 5)
|
|
from apps.complaints.models import Complaint
|
|
complaints = Complaint.objects.filter(source=source).select_related(
|
|
'patient', 'hospital', 'assigned_to'
|
|
).order_by('-created_at')[:5]
|
|
|
|
# Get inquiries from this source (recent 5)
|
|
from apps.complaints.models import Inquiry
|
|
inquiries = Inquiry.objects.filter(source=source).select_related(
|
|
'patient', 'hospital', 'assigned_to'
|
|
).order_by('-created_at')[:5]
|
|
|
|
# Calculate statistics
|
|
total_complaints = Complaint.objects.filter(source=source).count()
|
|
total_inquiries = Inquiry.objects.filter(source=source).count()
|
|
open_complaints = Complaint.objects.filter(source=source, status='open').count()
|
|
open_inquiries = Inquiry.objects.filter(source=source, status='open').count()
|
|
|
|
context = {
|
|
'source_user': source_user,
|
|
'source': source,
|
|
'complaints': complaints,
|
|
'inquiries': inquiries,
|
|
'total_complaints': total_complaints,
|
|
'total_inquiries': total_inquiries,
|
|
'open_complaints': open_complaints,
|
|
'open_inquiries': open_inquiries,
|
|
'can_create_complaints': source_user.can_create_complaints,
|
|
'can_create_inquiries': source_user.can_create_inquiries,
|
|
}
|
|
|
|
return render(request, 'px_sources/source_user_dashboard.html', context)
|
|
|
|
|
|
@login_required
|
|
def ajax_source_choices(request):
|
|
"""
|
|
AJAX endpoint for getting source choices for dropdowns
|
|
"""
|
|
queryset = PXSource.get_active_sources()
|
|
|
|
choices = [
|
|
{
|
|
'id': str(source.id),
|
|
'name_en': source.name_en,
|
|
'name_ar': source.name_ar,
|
|
}
|
|
for source in queryset
|
|
]
|
|
|
|
return JsonResponse({'choices': choices})
|
|
|
|
|
|
@login_required
|
|
def source_user_create(request, pk):
|
|
"""
|
|
Create a new source user for a specific PX source.
|
|
Only PX admins can create source users.
|
|
"""
|
|
# if not request.user.is_px_admin():
|
|
# messages.error(request, _("You don't have permission to create source users."))
|
|
# return redirect('px_sources:source_detail', pk=pk)
|
|
|
|
source = get_object_or_404(PXSource, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
user_id = request.POST.get('user')
|
|
user = get_object_or_404(User, pk=user_id)
|
|
|
|
try:
|
|
# Check if user already has a source user profile
|
|
if SourceUser.objects.filter(user=user).exists():
|
|
messages.error(request, _("User already has a source profile. A user can only manage one source."))
|
|
return redirect('px_sources:source_detail', pk=pk)
|
|
|
|
source_user = SourceUser.objects.create(
|
|
user=user,
|
|
source=source,
|
|
is_active=request.POST.get('is_active') == 'on',
|
|
can_create_complaints=request.POST.get('can_create_complaints') == 'on',
|
|
can_create_inquiries=request.POST.get('can_create_inquiries') == 'on',
|
|
)
|
|
|
|
messages.success(request, _("Source user created successfully!"))
|
|
return redirect('px_sources:source_detail', pk=pk)
|
|
|
|
except Exception as e:
|
|
messages.error(request, _("Error creating source user: {}").format(str(e)))
|
|
|
|
context = {
|
|
'source': source,
|
|
'available_users': User.objects.exclude(
|
|
id__in=source.source_users.values_list('user_id', flat=True)
|
|
).order_by('email'),
|
|
}
|
|
|
|
return render(request, 'px_sources/source_user_form.html', context)
|
|
|
|
|
|
@login_required
|
|
def source_user_edit(request, pk, user_pk):
|
|
"""
|
|
Edit an existing source user.
|
|
Only PX admins can edit source users.
|
|
"""
|
|
if not request.user.is_px_admin():
|
|
messages.error(request, _("You don't have permission to edit source users."))
|
|
return redirect('px_sources:source_detail', pk=pk)
|
|
|
|
source = get_object_or_404(PXSource, pk=pk)
|
|
source_user = get_object_or_404(SourceUser, pk=user_pk, source=source)
|
|
|
|
if request.method == 'POST':
|
|
try:
|
|
source_user.is_active = request.POST.get('is_active') == 'on'
|
|
source_user.can_create_complaints = request.POST.get('can_create_complaints') == 'on'
|
|
source_user.can_create_inquiries = request.POST.get('can_create_inquiries') == 'on'
|
|
source_user.save()
|
|
|
|
messages.success(request, _("Source user updated successfully!"))
|
|
return redirect('px_sources:source_detail', pk=pk)
|
|
|
|
except Exception as e:
|
|
messages.error(request, _("Error updating source user: {}").format(str(e)))
|
|
|
|
context = {
|
|
'source': source,
|
|
'source_user': source_user,
|
|
}
|
|
|
|
return render(request, 'px_sources/source_user_form.html', context)
|
|
|
|
|
|
@login_required
|
|
def source_user_delete(request, pk, user_pk):
|
|
"""
|
|
Delete a source user.
|
|
Only PX admins can delete source users.
|
|
"""
|
|
if not request.user.is_px_admin():
|
|
messages.error(request, _("You don't have permission to delete source users."))
|
|
return redirect('px_sources:source_detail', pk=pk)
|
|
|
|
source = get_object_or_404(PXSource, pk=pk)
|
|
source_user = get_object_or_404(SourceUser, pk=user_pk, source=source)
|
|
|
|
if request.method == 'POST':
|
|
user_name = source_user.user.get_full_name() or source_user.user.email
|
|
source_user.delete()
|
|
messages.success(request, _("Source user '{}' deleted successfully!").format(user_name))
|
|
return redirect('px_sources:source_detail', pk=pk)
|
|
|
|
context = {
|
|
'source': source,
|
|
'source_user': source_user,
|
|
}
|
|
|
|
return render(request, 'px_sources/source_user_confirm_delete.html', context)
|
|
|
|
|
|
@login_required
|
|
def source_user_toggle_status(request, pk, user_pk):
|
|
"""
|
|
Toggle source user active status (AJAX).
|
|
Only PX admins can toggle status.
|
|
"""
|
|
if not request.user.is_px_admin():
|
|
return JsonResponse({'error': 'Permission denied'}, status=403)
|
|
|
|
if request.method != 'POST':
|
|
return JsonResponse({'error': 'Method not allowed'}, status=405)
|
|
|
|
source = get_object_or_404(PXSource, pk=pk)
|
|
source_user = get_object_or_404(SourceUser, pk=user_pk, source=source)
|
|
|
|
source_user.is_active = not source_user.is_active
|
|
source_user.save()
|
|
|
|
return JsonResponse({
|
|
'success': True,
|
|
'is_active': source_user.is_active,
|
|
'message': 'Source user {} successfully'.format(
|
|
'activated' if source_user.is_active else 'deactivated'
|
|
)
|
|
})
|
|
|
|
|
|
@login_required
|
|
def source_user_complaint_list(request):
|
|
"""
|
|
List complaints for the current Source User.
|
|
Shows only complaints from their assigned source.
|
|
"""
|
|
# Get source user profile
|
|
source_user = SourceUser.get_active_source_user(request.user)
|
|
|
|
if not source_user:
|
|
messages.error(
|
|
request,
|
|
_("You are not assigned as a source user. Please contact your administrator.")
|
|
)
|
|
return redirect('/')
|
|
|
|
source = source_user.source
|
|
|
|
# Get complaints from this source
|
|
from apps.complaints.models import Complaint
|
|
from django.db.models import Q
|
|
|
|
complaints_queryset = Complaint.objects.filter(source=source).select_related(
|
|
'patient', 'hospital', 'assigned_to', 'created_by'
|
|
)
|
|
|
|
# Apply filters
|
|
status_filter = request.GET.get('status')
|
|
if status_filter:
|
|
complaints_queryset = complaints_queryset.filter(status=status_filter)
|
|
|
|
priority_filter = request.GET.get('priority')
|
|
if priority_filter:
|
|
complaints_queryset = complaints_queryset.filter(priority=priority_filter)
|
|
|
|
category_filter = request.GET.get('category')
|
|
if category_filter:
|
|
complaints_queryset = complaints_queryset.filter(category=category_filter)
|
|
|
|
# Search
|
|
search = request.GET.get('search')
|
|
if search:
|
|
complaints_queryset = complaints_queryset.filter(
|
|
Q(title__icontains=search) |
|
|
Q(description__icontains=search) |
|
|
Q(patient__first_name__icontains=search) |
|
|
Q(patient__last_name__icontains=search)
|
|
)
|
|
|
|
# Order and paginate
|
|
complaints_queryset = complaints_queryset.order_by('-created_at')
|
|
|
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
|
paginator = Paginator(complaints_queryset, 20) # 20 per page
|
|
page = request.GET.get('page')
|
|
try:
|
|
complaints = paginator.page(page)
|
|
except PageNotAnInteger:
|
|
complaints = paginator.page(1)
|
|
except EmptyPage:
|
|
complaints = paginator.page(paginator.num_pages)
|
|
|
|
context = {
|
|
'complaints': complaints,
|
|
'source_user': source_user,
|
|
'source': source,
|
|
'status_filter': status_filter,
|
|
'priority_filter': priority_filter,
|
|
'category_filter': category_filter,
|
|
'search': search,
|
|
'complaints_count': complaints_queryset.count(),
|
|
}
|
|
|
|
return render(request, 'px_sources/source_user_complaint_list.html', context)
|
|
|
|
|
|
@login_required
|
|
def source_user_inquiry_list(request):
|
|
"""
|
|
List inquiries for the current Source User.
|
|
Shows only inquiries from their assigned source.
|
|
"""
|
|
# Get source user profile
|
|
source_user = SourceUser.get_active_source_user(request.user)
|
|
|
|
if not source_user:
|
|
messages.error(
|
|
request,
|
|
_("You are not assigned as a source user. Please contact your administrator.")
|
|
)
|
|
return redirect('/')
|
|
|
|
source = source_user.source
|
|
|
|
# Get inquiries from this source
|
|
from apps.complaints.models import Inquiry
|
|
from django.db.models import Q
|
|
|
|
inquiries_queryset = Inquiry.objects.filter(source=source).select_related(
|
|
'patient', 'hospital', 'assigned_to', 'created_by'
|
|
)
|
|
|
|
# Apply filters
|
|
status_filter = request.GET.get('status')
|
|
if status_filter:
|
|
inquiries_queryset = inquiries_queryset.filter(status=status_filter)
|
|
|
|
category_filter = request.GET.get('category')
|
|
if category_filter:
|
|
inquiries_queryset = inquiries_queryset.filter(category=category_filter)
|
|
|
|
# Search
|
|
search = request.GET.get('search')
|
|
if search:
|
|
inquiries_queryset = inquiries_queryset.filter(
|
|
Q(subject__icontains=search) |
|
|
Q(message__icontains=search) |
|
|
Q(contact_name__icontains=search)
|
|
)
|
|
|
|
# Order and paginate
|
|
inquiries_queryset = inquiries_queryset.order_by('-created_at')
|
|
|
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
|
paginator = Paginator(inquiries_queryset, 20) # 20 per page
|
|
page = request.GET.get('page')
|
|
try:
|
|
inquiries = paginator.page(page)
|
|
except PageNotAnInteger:
|
|
inquiries = paginator.page(1)
|
|
except EmptyPage:
|
|
inquiries = paginator.page(paginator.num_pages)
|
|
|
|
context = {
|
|
'inquiries': inquiries,
|
|
'source_user': source_user,
|
|
'source': source,
|
|
'status_filter': status_filter,
|
|
'category_filter': category_filter,
|
|
'search': search,
|
|
'inquiries_count': inquiries_queryset.count(),
|
|
}
|
|
|
|
return render(request, 'px_sources/source_user_inquiry_list.html', context)
|