import logging from django import forms from django.contrib import admin from django.http import JsonResponse from access_core.monitoring.models import ( Alignment, CarePeriod, ClinicalTrack, DailyCheckinItem, DailyCheckinResponse, DailyCheckinSubmission, DailyCheckinTemplate, DailyStatusLog, EligibilityCheck, EligibilityRequest, EligibilityRequestCondition, EnrollmentDecision, MissedCheckinEvent, MonitoringPlan, MonitoringPlanItem, MonitoringPlanResult, MonitoringPlanTemplate, MonthlyPatientSummary, PatientAdherenceSummary, PatientExecutionLog, PatientNotification, ProtocolAssignmentVersion, ProtocolVersion, RawDailyCheckinSubmission, TrackSpecificDailyLog, Unalignment, ) logger = logging.getLogger(__name__) @admin.register(ClinicalTrack) class ClinicalTrackAdmin(admin.ModelAdmin): list_display = ["track_name", "description", "is_active"] class EligibilityRequestConditionForm(forms.ModelForm): """Marks the code input so icd10_autocomplete.js can attach a typeahead.""" class Meta: model = EligibilityRequestCondition fields = ["icd10_code", "icd10_description"] icd10_code = forms.CharField( max_length=10, widget=forms.TextInput(attrs={"class": "icd10-code-input"}), ) class EligibilityRequestConditionInline(admin.TabularInline): model = EligibilityRequestCondition form = EligibilityRequestConditionForm extra = 1 @admin.register(EligibilityRequest) class EligibilityRequestAdmin(admin.ModelAdmin): list_display = [ "intake_record", "clinical_track", "icd10_codes", "created_at", ] list_filter = ["clinical_track"] inlines = [EligibilityRequestConditionInline] @property def media(self): # select2 is intentionally NOT used here: its compat shim # replaces the ajax data adapter with a local-only lookup, so it can # never search on a plain text input. The typeahead is hand-rolled in # icd10_autocomplete.js (see that file's header for details). return super().media + forms.Media( js=("monitoring/icd10_autocomplete.js",), css={"screen": ("monitoring/icd10_autocomplete.css",)}, ) def icd10_search(self, request): """JSON typeahead endpoint searching the local ICD-10-CM snapshot table.""" from access_core.services.icd10_search import ( Icd10SearchError, search_icd10, ) terms = (request.GET.get("terms") or "").strip() if len(terms) < 2: return JsonResponse({"results": []}) try: results = search_icd10(terms) except Icd10SearchError as exc: logger.warning(f"ICD-10-CM search failed for {terms!r}: {exc}") # 5xx signals upstream failure; the JS ignores it and manual entry works. return JsonResponse({"results": [], "error": str(exc)}, status=502) return JsonResponse({"results": results}) def get_urls(self): from django.urls import path # Prepend: the built-in / history pattern would otherwise # swallow "icd10-search" as an object id. return [ path( "icd10-search/", self.admin_site.admin_view(self.icd10_search), name="monitoring_eligibilityrequest_icd10_search", ) ] + super().get_urls() @admin.display(description="ICD-10 codes") def icd10_codes(self, obj): return ", ".join(obj.conditions.values_list("icd10_code", flat=True)) or "—" @admin.register(EligibilityCheck) class EligibilityCheckAdmin(admin.ModelAdmin): list_display = [ "intake_record", "eligibility_request", "requested_track", "check_status", "checked_at", ] list_filter = ["check_status"] @admin.register(Alignment) class AlignmentAdmin(admin.ModelAdmin): list_display = ["intake_record", "clinical_track", "aligned_at"] @admin.register(Unalignment) class UnalignmentAdmin(admin.ModelAdmin): list_display = ["beneficiary", "unaligned_at", "reason"] @admin.register(EnrollmentDecision) class EnrollmentDecisionAdmin(admin.ModelAdmin): list_display = ["intake_record", "decision", "decision_by", "decision_at"] list_filter = ["decision"] @admin.register(CarePeriod) class CarePeriodAdmin(admin.ModelAdmin): list_display = [ "beneficiary", "clinical_track", "start_date", "end_date", "operational_status", ] list_filter = ["operational_status", "baseline_status"] @admin.register(ProtocolVersion) class ProtocolVersionAdmin(admin.ModelAdmin): list_display = ["version_number", "clinical_track", "is_active", "effective_from"] @admin.register(ProtocolAssignmentVersion) class ProtocolAssignmentVersionAdmin(admin.ModelAdmin): list_display = ["beneficiary", "protocol_version", "assigned_at"] @admin.register(PatientExecutionLog) class PatientExecutionLogAdmin(admin.ModelAdmin): list_display = ["beneficiary", "execution_date", "completed", "source_system"] list_filter = ["source_system"] @admin.register(DailyStatusLog) class DailyStatusLogAdmin(admin.ModelAdmin): list_display = ["beneficiary", "log_date", "source_system"] list_filter = ["source_system"] @admin.register(TrackSpecificDailyLog) class TrackSpecificDailyLogAdmin(admin.ModelAdmin): list_display = ["beneficiary", "log_date", "clinical_track", "source_system"] list_filter = ["clinical_track", "source_system"] @admin.register(MonitoringPlanTemplate) class MonitoringPlanTemplateAdmin(admin.ModelAdmin): list_display = ["template_name", "clinical_track", "is_active"] @admin.register(MonitoringPlan) class MonitoringPlanAdmin(admin.ModelAdmin): list_display = ["beneficiary", "clinical_trigger", "status", "created_at"] list_filter = ["status"] @admin.register(MonitoringPlanItem) class MonitoringPlanItemAdmin(admin.ModelAdmin): list_display = [ "monitoring_plan", "item_description", "frequency_interval", "status", ] list_filter = ["status"] @admin.register(MonitoringPlanResult) class MonitoringPlanResultAdmin(admin.ModelAdmin): list_display = [ "monitoring_plan_item", "result_date", "value_numeric", "review_status", ] @admin.register(DailyCheckinTemplate) class DailyCheckinTemplateAdmin(admin.ModelAdmin): list_display = ["template_name", "clinical_track", "version", "is_active"] @admin.register(DailyCheckinItem) class DailyCheckinItemAdmin(admin.ModelAdmin): list_display = ["template", "item_key", "item_type", "display_order"] @admin.register(DailyCheckinSubmission) class DailyCheckinSubmissionAdmin(admin.ModelAdmin): list_display = ["beneficiary", "submission_date", "source_system", "status"] @admin.register(DailyCheckinResponse) class DailyCheckinResponseAdmin(admin.ModelAdmin): list_display = ["submission", "item"] @admin.register(RawDailyCheckinSubmission) class RawDailyCheckinSubmissionAdmin(admin.ModelAdmin): list_display = ["beneficiary", "source_system", "ingested_at"] @admin.register(MissedCheckinEvent) class MissedCheckinEventAdmin(admin.ModelAdmin): list_display = ["beneficiary", "missed_date", "consecutive_days_missed", "notified"] @admin.register(PatientAdherenceSummary) class PatientAdherenceSummaryAdmin(admin.ModelAdmin): list_display = ["beneficiary", "period_start", "period_end", "adherence_rate"] @admin.register(MonthlyPatientSummary) class MonthlyPatientSummaryAdmin(admin.ModelAdmin): list_display = ["beneficiary", "month", "review_status"] @admin.register(PatientNotification) class PatientNotificationAdmin(admin.ModelAdmin): list_display = ["beneficiary", "notification_type", "channel", "sent_at"] list_filter = ["notification_type", "channel"]