""" Management command to manually fetch patient data from HIS system. Usage: python manage.py fetch_his_surveys [--minutes N] [--from-date DATE] [--to-date DATE] [--test] Options: --minutes N Fetch patients from the last N minutes (default: 10) --from-date DATE Fetch patients from this date (DD-Mon-YYYY HH:MM:SS) --to-date DATE Fetch patients until this date (DD-Mon-YYYY HH:MM:SS) --test Test connection only, don't fetch data --config NAME Configuration name to use (optional) Examples: # Relative: last 10 minutes python manage.py fetch_his_surveys --minutes 10 # Absolute: simulate a date range (for testing against test endpoint) python manage.py fetch_his_surveys --config "HIS Test" \ --from-date "01-Jan-2026 00:00:00" --to-date "01-Jan-2026 02:00:00" # Test connection python manage.py fetch_his_surveys --config "HIS Test" --test """ from django.core.management.base import BaseCommand from django.utils import timezone from apps.integrations.services.his_client import HISClient, HISClientFactory from apps.integrations.services.his_adapter import HISAdapter class Command(BaseCommand): help = "Fetch patient data from HIS system" def add_arguments(self, parser): parser.add_argument( "--minutes", type=int, default=10, help="Fetch patients from the last N minutes (default: 10)", ) parser.add_argument( "--from-date", type=str, default=None, help="Fetch patients from this date (DD-Mon-YYYY HH:MM:SS)", ) parser.add_argument( "--to-date", type=str, default=None, help="Fetch patients until this date (DD-Mon-YYYY HH:MM:SS)", ) parser.add_argument( "--test", action="store_true", help="Test connection only, don't fetch data", ) parser.add_argument( "--config", type=str, help="Configuration name to use (optional)", ) def handle(self, *args, **options): minutes = options["minutes"] from_date_str = options.get("from_date") to_date_str = options.get("to_date") test_only = options["test"] config_name = options.get("config") self.stdout.write(self.style.MIGRATE_HEADING("=" * 70)) self.stdout.write(self.style.MIGRATE_HEADING("HIS Patient Data Fetch")) self.stdout.write(self.style.MIGRATE_HEADING("=" * 70)) if from_date_str and to_date_str: self.stdout.write(f"Mode: custom date range") self.stdout.write(f" From: {from_date_str}") self.stdout.write(f" To: {to_date_str}") else: self.stdout.write(f"Mode: relative (last {minutes} minutes)") if config_name: from apps.integrations.models import IntegrationConfig config = IntegrationConfig.objects.filter(name=config_name).first() if not config: self.stdout.write(self.style.ERROR(f'Configuration "{config_name}" not found')) return clients = [HISClient(config)] else: clients = HISClientFactory.get_all_active_clients() if not clients: self.stdout.write(self.style.ERROR("No active HIS configurations found")) return self.stdout.write(f"Found {len(clients)} HIS configuration(s)") total_patients = 0 total_visits_saved = 0 total_surveys_created = 0 for client in clients: config_display = client.config.name if client.config else "Default" self.stdout.write(self.style.MIGRATE_HEADING(f"\nConfiguration: {config_display}")) self.stdout.write("Testing connection...", ending=" ") test_result = client.test_connection() if test_result["success"]: self.stdout.write(self.style.SUCCESS("Connected")) else: self.stdout.write(self.style.ERROR(f"Failed: {test_result['message']}")) continue if test_only: continue from datetime import timedelta from apps.integrations.tasks import _parse_his_date if from_date_str and to_date_str: fetch_since = _parse_his_date(from_date_str) fetch_until = _parse_his_date(to_date_str) if not fetch_since or not fetch_until: self.stdout.write(self.style.ERROR("Invalid date format. Use DD-Mon-YYYY HH:MM:SS")) return time_display = f"{fetch_since.strftime('%Y-%m-%d %H:%M')} to {fetch_until.strftime('%Y-%m-%d %H:%M')}" else: fetch_since = timezone.now() - timedelta(minutes=minutes) fetch_until = None time_display = f"{fetch_since.strftime('%Y-%m-%d %H:%M:%S')}" self.stdout.write(f"Fetching patient data: {time_display}") his_data = client.fetch_patient_data(since=fetch_since, until=fetch_until) if not his_data: self.stdout.write(self.style.WARNING("No data returned from HIS")) continue patient_list = his_data.get("FetchPatientDataTimeStampList", []) if not patient_list: self.stdout.write(self.style.WARNING("No patients found")) continue self.stdout.write(f"Found {len(patient_list)} patient(s)") process_result = HISAdapter.process_his_response(his_data) total_patients += len(patient_list) total_visits_saved += process_result.get("visits_saved", 0) total_surveys_created += process_result.get("surveys_created", 0) for detail in process_result.get("details", []): name = detail.get("patient_name", "Unknown") ptype = detail.get("patient_type", "") adm_id = detail.get("admission_id", "") if detail.get("survey_created"): self.stdout.write(f" {self.style.SUCCESS('+')} {name} ({ptype}) adm={adm_id} - survey created") elif detail.get("error"): self.stdout.write(f" {self.style.ERROR('x')} {name} ({ptype}) adm={adm_id} - {detail['error']}") else: reason = detail.get("reason", "Visit in progress") self.stdout.write(f" - {name} ({ptype}) adm={adm_id} - {reason}") if process_result.get("errors"): for err in process_result["errors"]: self.stdout.write(self.style.WARNING(f" Error: {err}")) if client.config: client.config.last_sync_at = timezone.now() client.config.save(update_fields=["last_sync_at"]) self.stdout.write(self.style.MIGRATE_HEADING("\n" + "=" * 70)) self.stdout.write(self.style.MIGRATE_HEADING("Summary")) self.stdout.write(self.style.MIGRATE_HEADING("=" * 70)) self.stdout.write(f"Total patients fetched: {total_patients}") self.stdout.write(f"Visits saved/updated: {total_visits_saved}") self.stdout.write(f"Surveys created: {total_surveys_created}") if test_only: self.stdout.write(self.style.SUCCESS("\nConnection test completed successfully!")) else: self.stdout.write(self.style.SUCCESS("\nFetch completed!"))