from rest_framework import viewsets, status from rest_framework.decorators import action from rest_framework.response import Response from django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth.decorators import login_required from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt from django.http import JsonResponse from django.db.models import Count, Q from django.utils import timezone from datetime import datetime import json from apps.standards.models import StandardSource, StandardCategory, Standard, StandardCompliance, StandardAttachment, ActivityType from apps.organizations.models import Department from apps.standards.forms import ( StandardSourceForm, StandardCategoryForm, StandardForm, StandardComplianceForm, StandardAttachmentForm, ActivityTypeForm, ) # ==================== API ViewSets ==================== class StandardSourceViewSet(viewsets.ModelViewSet): queryset = StandardSource.objects.all() filterset_fields = ["is_active"] search_fields = ["name", "name_ar", "code"] ordering = ["name"] def get_serializer_class(self): from apps.standards.serializers import StandardSourceSerializer return StandardSourceSerializer def get_queryset(self): queryset = super().get_queryset() user = self.request.user if user.is_px_admin(): return queryset if user.hospital: return queryset.filter(standards__departments__hospital=user.hospital) return queryset.none() class StandardCategoryViewSet(viewsets.ModelViewSet): queryset = StandardCategory.objects.all() filterset_fields = ["is_active", "source"] search_fields = ["name", "name_ar"] ordering = ["order", "name"] def get_serializer_class(self): from apps.standards.serializers import StandardCategorySerializer return StandardCategorySerializer def get_queryset(self): queryset = super().get_queryset() user = self.request.user if user.is_px_admin(): return queryset if user.hospital: return queryset.filter(standards__departments__hospital=user.hospital) return queryset.none() class StandardViewSet(viewsets.ModelViewSet): queryset = Standard.objects.all() filterset_fields = ["source", "category", "activity_type", "is_active"] search_fields = ["code", "title", "title_ar", "description"] ordering = ["source", "category", "code"] def get_serializer_class(self): from apps.standards.serializers import StandardSerializer return StandardSerializer def get_queryset(self): queryset = super().get_queryset() user = self.request.user if user.is_px_admin(): return queryset if user.hospital: return queryset.filter(department__hospital=user.hospital) return queryset.none() class StandardComplianceViewSet(viewsets.ModelViewSet): queryset = StandardCompliance.objects.all() filterset_fields = ["department", "standard", "status"] search_fields = ["department__name", "standard__code", "notes"] ordering = ["-created_at"] def get_serializer_class(self): from apps.standards.serializers import StandardComplianceSerializer return StandardComplianceSerializer def get_queryset(self): queryset = super().get_queryset() user = self.request.user if user.is_px_admin(): return queryset if user.hospital: return queryset.filter(department__hospital=user.hospital) return queryset.none() class StandardAttachmentViewSet(viewsets.ModelViewSet): queryset = StandardAttachment.objects.all() filterset_fields = ["compliance"] search_fields = ["filename", "description"] ordering = ["-uploaded_at"] def get_serializer_class(self): from apps.standards.serializers import StandardAttachmentSerializer return StandardAttachmentSerializer def get_queryset(self): queryset = super().get_queryset() user = self.request.user if user.is_px_admin(): return queryset if user.hospital: return queryset.filter(departments__hospital=user.hospital) return queryset.none() # ==================== UI Views ==================== @login_required def standards_dashboard(request): """Standards dashboard with statistics""" # Get current hospital from tenant_hospital (set by middleware) hospital = getattr(request, "tenant_hospital", None) if not hospital: return render(request, "core/no_hospital_assigned.html") departments = hospital.departments.filter(status="active") # Get compliance statistics compliance_records = StandardCompliance.objects.filter(department__hospital=hospital) stats = { "total_standards": Standard.objects.filter(is_active=True).count(), "total_departments": departments.count(), "met": compliance_records.filter(status="met").count(), "partially_met": compliance_records.filter(status="partially_met").count(), "not_met": compliance_records.filter(status="not_met").count(), "not_assessed": compliance_records.filter(status="not_assessed").count(), } # Recent compliance updates recent_updates = compliance_records.order_by("-updated_at")[:10] # Check if user is PX admin or Hospital Admin is_px_admin = request.user.is_px_admin() or request.user.is_hospital_admin() context = { "hospital": hospital, "departments": departments, "stats": stats, "recent_updates": recent_updates, "is_px_admin": is_px_admin, } return render(request, "standards/dashboard.html", context) @ensure_csrf_cookie @login_required def department_standards_view(request, pk): """View all standards for a department""" department = get_object_or_404(Department, pk=pk) # Check if user is PX admin hospital = getattr(request, "tenant_hospital", None) is_px_admin = (request.user.is_px_admin() or request.user.is_hospital_admin()) if hospital else False department_standards = ( Standard.objects.filter(is_active=True) .annotate(dept_count=Count("departments")) .filter(Q(departments=department) | Q(dept_count=0)) .select_related("source", "category", "parent_standard") .prefetch_related("sub_standards") .order_by("source", "category", "order_within_category", "code") ) parent_standards = department_standards.filter(parent_standard__isnull=True) standards_data = [] for standard in parent_standards: compliance = StandardCompliance.objects.filter(department=department, standard=standard).first() sub_standards_data = [] for sub in standard.sub_standards.filter(is_active=True).order_by("order_within_category", "code"): sub_compliance = StandardCompliance.objects.filter(department=department, standard=sub).first() sub_standards_data.append({ "standard": sub, "compliance": sub_compliance, "attachment_count": sub_compliance.attachments.count() if sub_compliance else 0, "is_substandard": True, }) standards_data.append( { "standard": standard, "compliance": compliance, "attachment_count": compliance.attachments.count() if compliance else 0, "is_substandard": False, "sub_standards": sub_standards_data, "has_sub_standards": len(sub_standards_data) > 0, } ) orphan_substandards = department_standards.filter( parent_standard__isnull=False ).exclude(parent_standard__in=parent_standards) for sub in orphan_substandards: sub_compliance = StandardCompliance.objects.filter(department=department, standard=sub).first() standards_data.append({ "standard": sub, "compliance": sub_compliance, "attachment_count": sub_compliance.attachments.count() if sub_compliance else 0, "is_substandard": True, "sub_standards": [], "has_sub_standards": False, }) context = { "department": department, "standards_data": standards_data, "is_px_admin": is_px_admin, } return render(request, "standards/department_standards.html", context) @login_required def standard_detail(request, pk): """View standard details and compliance history""" standard = get_object_or_404(Standard, pk=pk) compliance_records = ( StandardCompliance.objects.filter(standard=standard) .select_related("department", "assessor") .annotate(attachment_count=Count("attachments")) .order_by("-created_at") ) hospital = getattr(request, "tenant_hospital", None) is_px_admin = False if hospital: is_px_admin = request.user.is_px_admin() or request.user.is_hospital_admin() or request.user.is_px_management() departments = Department.objects.filter(status="active").order_by("name") if hospital: departments = departments.filter(hospital=hospital) parent_standard = standard.parent_standard sub_standards = standard.sub_standards.filter(is_active=True).select_related("source", "category") context = { "standard": standard, "compliance_records": compliance_records, "is_px_admin": is_px_admin, "parent_standard": parent_standard, "sub_standards": sub_standards, "departments": departments, "hospital": hospital, } return render(request, "standards/standard_detail.html", context) @login_required def standard_compliance_update(request, compliance_id): """Update compliance status""" if not (request.user.is_px_admin() or request.user.is_hospital_admin() or request.user.is_px_management()): messages.error(request, "You do not have permission to update compliance assessments.") return redirect("standards:standard_detail", pk=compliance_id) compliance = get_object_or_404(StandardCompliance, pk=compliance_id) if request.method == "POST": form = StandardComplianceForm(request.POST, instance=compliance) if form.is_valid(): form.save() return redirect("standards:department_standards", pk=compliance.department.pk) else: form = StandardComplianceForm(instance=compliance) context = { "compliance": compliance, "form": form, } return render(request, "standards/compliance_form.html", context) @login_required def standard_attachment_upload(request, compliance_id): """Upload attachment for compliance""" if not (request.user.is_px_admin() or request.user.is_hospital_admin() or request.user.is_px_management()): messages.error(request, "You do not have permission to upload evidence.") return redirect("standards:standard_detail", pk=compliance_id) compliance = get_object_or_404(StandardCompliance, pk=compliance_id) if request.method == "POST": form = StandardAttachmentForm(request.POST, request.FILES) if form.is_valid(): attachment = form.save(commit=False) attachment.compliance = compliance attachment.uploaded_by = request.user attachment.filename = request.FILES["file"].name attachment.save() return redirect("standards:standard_compliance_update", compliance_id=compliance.pk) else: form = StandardAttachmentForm() context = { "compliance": compliance, "form": form, } return render(request, "standards/attachment_upload.html", context) @login_required def standard_attachment_delete(request, pk): """Delete an attachment""" attachment = get_object_or_404(StandardAttachment, pk=pk) compliance_id = attachment.compliance.id if request.method == "POST": attachment.delete() from django.contrib import messages messages.success(request, "Attachment deleted successfully.") return redirect("standards:standard_compliance_update", compliance_id=compliance_id) context = {"attachment": attachment} return render(request, "standards/attachment_confirm_delete.html", context) @login_required def standards_search(request): """Search standards""" query = request.GET.get("q", "") source_filter = request.GET.get("source", "") category_filter = request.GET.get("category", "") status_filter = request.GET.get("status", "") department_filter = request.GET.get("department", "") hospital = getattr(request, "tenant_hospital", None) if not hospital: return render(request, "core/no_hospital_assigned.html") standards = Standard.objects.filter(is_active=True) if query: standards = standards.filter( Q(code__icontains=query) | Q(title__icontains=query) | Q(title_ar__icontains=query) | Q(description__icontains=query) ) if source_filter: standards = standards.filter(source_id=source_filter) if category_filter: standards = standards.filter(category_id=category_filter) if department_filter: department = Department.objects.filter(pk=department_filter, hospital=hospital).first() else: department = None standards = standards.select_related("source", "category", "parent_standard").prefetch_related("sub_standards").order_by("source", "category", "order_within_category", "code") sources = StandardSource.objects.filter(is_active=True) categories = StandardCategory.objects.filter(is_active=True) if source_filter: categories = categories.filter(source_id=source_filter) is_px_admin = request.user.is_px_admin() or request.user.is_hospital_admin() standards_data = None if department: parent_standards = standards.filter(parent_standard__isnull=True) standards_data = [] for standard in parent_standards: compliance = StandardCompliance.objects.filter(department=department, standard=standard).first() sub_standards_data = [] for sub in standard.sub_standards.filter(is_active=True).order_by("order_within_category", "code"): sub_compliance = StandardCompliance.objects.filter(department=department, standard=sub).first() sub_standards_data.append({ "standard": sub, "compliance": sub_compliance, "attachment_count": sub_compliance.attachments.count() if sub_compliance else 0, "is_substandard": True, }) standards_data.append({ "standard": standard, "compliance": compliance, "attachment_count": compliance.attachments.count() if compliance else 0, "is_substandard": False, "sub_standards": sub_standards_data, "has_sub_standards": len(sub_standards_data) > 0, }) orphan_substandards = standards.filter( parent_standard__isnull=False ).exclude(parent_standard__in=parent_standards) for sub in orphan_substandards: sub_compliance = StandardCompliance.objects.filter(department=department, standard=sub).first() standards_data.append({ "standard": sub, "compliance": sub_compliance, "attachment_count": sub_compliance.attachments.count() if sub_compliance else 0, "is_substandard": True, "sub_standards": [], "has_sub_standards": False, }) departments = hospital.departments.filter(status="active").order_by("name") context = { "hospital": hospital, "standards": standards, "standards_data": standards_data, "department": department, "departments": departments, "department_filter": department_filter, "query": query, "source_filter": source_filter, "category_filter": category_filter, "sources": sources, "categories": categories, "all_categories": StandardCategory.objects.filter(is_active=True), "is_px_admin": is_px_admin, } return render(request, "standards/search.html", context) @login_required def standard_create(request, department_id=None): """Create a new standard (PX Admin only)""" # Check if user is PX admin hospital = getattr(request, "tenant_hospital", None) if not hospital: return render(request, "core/no_hospital_assigned.html") is_px_admin = request.user.is_px_admin() or request.user.is_hospital_admin() if not is_px_admin: from django.contrib import messages messages.error(request, "You do not have permission to create standards.") if department_id: return redirect("standards:department_standards", pk=department_id) return redirect("standards:dashboard") if request.method == "POST": form = StandardForm(request.POST) if form.is_valid(): standard = form.save() from django.contrib import messages messages.success(request, "Standard created successfully.") if department_id: return redirect("standards:department_standards", pk=department_id) return redirect("standards:search") else: form = StandardForm() # If department_id is provided, pre-select that department if department_id: from apps.organizations.models import Department department = Department.objects.filter(pk=department_id).first() if department: form.fields["departments"].initial = [department] # Get all departments for the hospital departments = hospital.departments.filter(status="active") from apps.standards.models import StandardCategory import json all_categories = list( StandardCategory.objects.filter(is_active=True).values("id", "name", "source_id").order_by("order", "name") ) categories_json = json.dumps(all_categories, default=str) parent_standards = list( Standard.objects.filter(is_active=True, parent_standard__isnull=True) .values("id", "code", "title", "source_id") .order_by("source", "code") ) parents_json = json.dumps(parent_standards, default=str) context = { "form": form, "department_id": department_id, "departments": departments, "hospital": hospital, "categories_json": categories_json, "parents_json": parents_json, } return render(request, "standards/standard_form.html", context) @login_required def standard_update(request, pk): """Update a standard (PX Admin only)""" standard = get_object_or_404(Standard, pk=pk) # Check if user is PX admin hospital = getattr(request, "tenant_hospital", None) if not hospital: return render(request, "core/no_hospital_assigned.html") is_px_admin = request.user.is_px_admin() or request.user.is_hospital_admin() if not is_px_admin: from django.contrib import messages messages.error(request, "You do not have permission to update standards.") return redirect("standards:standard_detail", pk=pk) if request.method == "POST": form = StandardForm(request.POST, instance=standard) if form.is_valid(): form.save() from django.contrib import messages messages.success(request, "Standard updated successfully.") return redirect("standards:standard_detail", pk=pk) else: form = StandardForm(instance=standard) departments = hospital.departments.filter(status="active") from apps.standards.models import StandardCategory import json all_categories = list( StandardCategory.objects.filter(is_active=True).values("id", "name", "source_id").order_by("order", "name") ) categories_json = json.dumps(all_categories, default=str) parent_standards = list( Standard.objects.filter(is_active=True, parent_standard__isnull=True) .exclude(pk=standard.pk) .values("id", "code", "title", "source_id") .order_by("source", "code") ) parents_json = json.dumps(parent_standards, default=str) context = { "form": form, "standard": standard, "hospital": hospital, "departments": departments, "categories_json": categories_json, "parents_json": parents_json, } return render(request, "standards/standard_form.html", context) @login_required def standard_delete(request, pk): """Delete a standard (PX Admin only)""" standard = get_object_or_404(Standard, pk=pk) # Check if user is PX admin hospital = getattr(request, "tenant_hospital", None) if not hospital: return render(request, "core/no_hospital_assigned.html") is_px_admin = request.user.is_px_admin() or request.user.is_hospital_admin() if not is_px_admin: from django.contrib import messages messages.error(request, "You do not have permission to delete standards.") return redirect("standards:standard_detail", pk=pk) if request.method == "POST": standard.delete() from django.contrib import messages messages.success(request, "Standard deleted successfully.") return redirect("standards:search") context = {"standard": standard} return render(request, "standards/standard_confirm_delete.html", context) @ensure_csrf_cookie @login_required def create_compliance_ajax(request): """Create compliance record via AJAX""" if not request.user.is_authenticated: return JsonResponse({"success": False, "error": "Authentication required"}, status=401) if request.method != "POST": return JsonResponse({"success": False, "error": "Invalid request method"}) # Parse JSON from request body try: data = json.loads(request.body) except json.JSONDecodeError: return JsonResponse({"success": False, "error": "Invalid JSON"}) department_id = data.get("department_id") standard_id = data.get("standard_id") if not department_id or not standard_id: return JsonResponse({"success": False, "error": "Missing required fields"}) try: department = Department.objects.get(pk=department_id) standard = Standard.objects.get(pk=standard_id) # Check if compliance already exists compliance, created = StandardCompliance.objects.get_or_create( department=department, standard=standard, defaults={ "assessor": request.user, "last_assessed_date": timezone.now().date(), }, ) return JsonResponse( { "success": True, "compliance_id": compliance.id, "status": compliance.status, "created": created, } ) except Exception as e: import traceback traceback.print_exc() return JsonResponse({"success": False, "error": str(e)}) @ensure_csrf_cookie @login_required def update_compliance_ajax(request): """Update or create compliance record via AJAX""" if not request.user.is_authenticated: return JsonResponse({"success": False, "error": "Authentication required"}, status=401) if request.method != "POST": return JsonResponse({"success": False, "error": "Invalid request method"}) # Parse JSON from request body try: data = json.loads(request.body) except json.JSONDecodeError: return JsonResponse({"success": False, "error": "Invalid JSON"}) compliance_id = data.get("compliance_id") department_id = data.get("department_id") standard_id = data.get("standard_id") status = data.get("status") notes = data.get("notes", "") evidence_summary = data.get("evidence_summary", "") last_assessed_date_str = data.get("last_assessed_date") assessor_id = data.get("assessor_id") # Mode 1: update_or_create by department + standard (from department detail page) if department_id and standard_id: if not status: return JsonResponse({"success": False, "error": "Missing status"}) try: department = Department.objects.get(pk=department_id) standard = Standard.objects.get(pk=standard_id) hospital = getattr(request, "tenant_hospital", None) compliance, created = StandardCompliance.objects.update_or_create( hospital=hospital, standard=standard, defaults={ "department": department, "status": status, "notes": notes, "evidence_summary": evidence_summary, "last_assessed_date": timezone.now().date(), "assessor": request.user, }, ) return JsonResponse( { "success": True, "created": created, "compliance_id": str(compliance.id), "status": compliance.status, "status_display": compliance.get_status_display(), } ) except Exception as e: import traceback traceback.print_exc() return JsonResponse({"success": False, "error": str(e)}) # Mode 2: update existing compliance record by compliance_id if not compliance_id or not status: return JsonResponse({"success": False, "error": "Missing required fields"}) try: compliance = StandardCompliance.objects.get(pk=compliance_id) compliance.status = status compliance.notes = notes compliance.evidence_summary = evidence_summary # Set assessor - use logged-in user or provided ID if assessor_id: from apps.accounts.models import User try: assessor = User.objects.get(pk=assessor_id) compliance.assessor = assessor except User.DoesNotExist: compliance.assessor = request.user else: compliance.assessor = request.user # Set assessment date if last_assessed_date_str: compliance.last_assessed_date = datetime.strptime(last_assessed_date_str, "%Y-%m-%d").date() else: compliance.last_assessed_date = timezone.now().date() compliance.save() return JsonResponse( { "success": True, "status": compliance.status, "status_display": compliance.get_status_display(), } ) except Exception as e: import traceback traceback.print_exc() return JsonResponse({"success": False, "error": str(e)}) @login_required def get_compliance_status(request, department_id, standard_id): """API endpoint to get compliance status""" compliance = StandardCompliance.objects.filter(department_id=department_id, standard_id=standard_id).first() if compliance: data = { "status": compliance.status, "last_assessed_date": compliance.last_assessed_date, "assessor": compliance.assessor.get_full_name() if compliance.assessor else None, "notes": compliance.notes, "attachment_count": compliance.attachments.count(), } else: data = { "status": "not_assessed", "last_assessed_date": None, "assessor": None, "notes": "", "attachment_count": 0, } return JsonResponse(data) @ensure_csrf_cookie @login_required def get_attachments_ajax(request, compliance_id): """Get all attachments for a compliance record via AJAX""" if not request.user.is_authenticated: return JsonResponse({"success": False, "error": "Authentication required"}, status=401) compliance = get_object_or_404(StandardCompliance, pk=compliance_id) attachments = compliance.attachments.select_related("uploaded_by").order_by("-created_at") attachments_data = [] for attachment in attachments: attachments_data.append( { "id": str(attachment.id), "filename": attachment.filename, "description": attachment.description or "", "file_url": attachment.file.url, "uploaded_by": attachment.uploaded_by.get_full_name() if attachment.uploaded_by else "Unknown", "uploaded_at": attachment.created_at.strftime("%Y-%m-%d %H:%M"), } ) return JsonResponse( { "success": True, "compliance_id": str(compliance.id), "standard_code": compliance.standard.code, "standard_title": compliance.standard.title, "attachments": attachments_data, "attachment_count": len(attachments_data), } ) @ensure_csrf_cookie @login_required def upload_attachment_ajax(request): """Upload attachment for compliance via AJAX""" if not request.user.is_authenticated: return JsonResponse({"success": False, "error": "Authentication required"}, status=401) if request.method != "POST": return JsonResponse({"success": False, "error": "Only POST method allowed"}) compliance_id = request.POST.get("compliance_id") standard_id = request.POST.get("standard_id") department_id = request.POST.get("department_id") description = request.POST.get("description", "") if "file" not in request.FILES: return JsonResponse({"success": False, "error": "No file provided"}) uploaded_file = request.FILES["file"] # Validate file size (max 50MB) if uploaded_file.size > 50 * 1024 * 1024: return JsonResponse({"success": False, "error": "File size must be less than 50MB"}) # Validate file type allowed_extensions = [".pdf", ".doc", ".docx", ".xls", ".xlsx", ".jpg", ".jpeg", ".png", ".zip"] file_ext = "." + uploaded_file.name.split(".")[-1].lower() if "." in uploaded_file.name else "" if file_ext not in allowed_extensions: return JsonResponse( { "success": False, "error": f"Invalid file type. Allowed: {', '.join(allowed_extensions)}", } ) try: # Mode 1: by compliance_id (existing) if compliance_id: compliance = get_object_or_404(StandardCompliance, pk=compliance_id) # Mode 2: by standard_id + department_id (auto-create compliance) elif standard_id and department_id: from apps.organizations.models import Department standard = get_object_or_404(Standard, pk=standard_id) department = get_object_or_404(Department, pk=department_id) hospital = getattr(request, "tenant_hospital", None) compliance, _ = StandardCompliance.objects.get_or_create( hospital=hospital, standard=standard, defaults={ "department": department, "status": StandardCompliance.ComplianceStatus.NOT_ASSESSED, "last_assessed_date": timezone.now().date(), "assessor": request.user, }, ) else: return JsonResponse({"success": False, "error": "Missing compliance_id or standard_id+department_id"}) attachment = StandardAttachment.objects.create( compliance=compliance, file=uploaded_file, filename=uploaded_file.name, description=description, uploaded_by=request.user, ) return JsonResponse( { "success": True, "attachment": { "id": str(attachment.id), "filename": attachment.filename, "description": attachment.description or "", "file_url": attachment.file.url, "uploaded_by": request.user.get_full_name() if request.user else "Unknown", "uploaded_at": attachment.created_at.strftime("%Y-%m-%d %H:%M"), }, "attachment_count": compliance.attachments.count(), "compliance_id": str(compliance.id), } ) except Exception as e: import traceback traceback.print_exc() return JsonResponse({"success": False, "error": str(e)}) @ensure_csrf_cookie @login_required def delete_attachment_ajax(request, attachment_id): """Delete an attachment via AJAX""" if not request.user.is_authenticated: return JsonResponse({"success": False, "error": "Authentication required"}, status=401) if request.method != "POST": return JsonResponse({"success": False, "error": "Only POST method allowed"}) attachment = get_object_or_404(StandardAttachment, pk=attachment_id) compliance = attachment.compliance try: attachment.delete() return JsonResponse({"success": True, "attachment_count": compliance.attachments.count()}) except Exception as e: import traceback traceback.print_exc() return JsonResponse({"success": False, "error": str(e)}) # ==================== Source Management Views ==================== @login_required def source_list(request): """List all standard sources""" sources = StandardSource.objects.all().order_by("name") context = {"sources": sources} return render(request, "standards/source_list.html", context) @login_required def source_create(request): """Create a new standard source""" if request.method == "POST": form = StandardSourceForm(request.POST) if form.is_valid(): form.save() from django.contrib import messages messages.success(request, "Source created successfully.") return redirect("standards:source_list") else: form = StandardSourceForm() context = {"form": form} return render(request, "standards/source_form.html", context) @login_required def source_update(request, pk): """Update a standard source""" source = get_object_or_404(StandardSource, pk=pk) if request.method == "POST": form = StandardSourceForm(request.POST, instance=source) if form.is_valid(): form.save() from django.contrib import messages messages.success(request, "Source updated successfully.") return redirect("standards:source_list") else: form = StandardSourceForm(instance=source) context = {"form": form, "source": source} return render(request, "standards/source_form.html", context) @login_required def source_delete(request, pk): """Delete a standard source""" source = get_object_or_404(StandardSource, pk=pk) if request.method == "POST": source.delete() from django.contrib import messages messages.success(request, "Source deleted successfully.") return redirect("standards:source_list") context = {"source": source} return render(request, "standards/source_confirm_delete.html", context) # ==================== Category Management Views ==================== @login_required def category_list(request): """List all standard categories""" categories = StandardCategory.objects.all().order_by("order", "name") context = {"categories": categories} return render(request, "standards/category_list.html", context) @login_required def category_create(request): """Create a new standard category""" if request.method == "POST": form = StandardCategoryForm(request.POST) if form.is_valid(): form.save() from django.contrib import messages messages.success(request, "Category created successfully.") return redirect("standards:category_list") else: form = StandardCategoryForm() context = {"form": form} return render(request, "standards/category_form.html", context) @login_required def category_update(request, pk): """Update a standard category""" category = get_object_or_404(StandardCategory, pk=pk) if request.method == "POST": form = StandardCategoryForm(request.POST, instance=category) if form.is_valid(): form.save() from django.contrib import messages messages.success(request, "Category updated successfully.") return redirect("standards:category_list") else: form = StandardCategoryForm(instance=category) context = {"form": form, "category": category} return render(request, "standards/category_form.html", context) @login_required def category_delete(request, pk): """Delete a standard category""" category = get_object_or_404(StandardCategory, pk=pk) if request.method == "POST": category.delete() from django.contrib import messages messages.success(request, "Category deleted successfully.") return redirect("standards:category_list") context = {"category": category} return render(request, "standards/category_confirm_delete.html", context) # ==================== Activity Type Management Views ==================== @login_required def activity_type_list(request): """List all activity types""" activity_types = ActivityType.objects.all().order_by("name") context = {"activity_types": activity_types} return render(request, "standards/activity_type_list.html", context) @login_required def activity_type_create(request): """Create a new activity type""" if request.method == "POST": form = ActivityTypeForm(request.POST) if form.is_valid(): form.save() from django.contrib import messages messages.success(request, "Activity type created successfully.") return redirect("standards:activity_type_list") else: form = ActivityTypeForm() context = {"form": form} return render(request, "standards/activity_type_form.html", context) @login_required def activity_type_update(request, pk): """Update an activity type""" activity_type = get_object_or_404(ActivityType, pk=pk) if request.method == "POST": form = ActivityTypeForm(request.POST, instance=activity_type) if form.is_valid(): form.save() from django.contrib import messages messages.success(request, "Activity type updated successfully.") return redirect("standards:activity_type_list") else: form = ActivityTypeForm(instance=activity_type) context = {"form": form, "activity_type": activity_type} return render(request, "standards/activity_type_form.html", context) @login_required def activity_type_delete(request, pk): """Delete an activity type""" activity_type = get_object_or_404(ActivityType, pk=pk) if request.method == "POST": activity_type.delete() from django.contrib import messages messages.success(request, "Activity type deleted successfully.") return redirect("standards:activity_type_list") context = {"activity_type": activity_type} return render(request, "standards/activity_type_confirm_delete.html", context)