HH/apps/integrations/management/commands/fetch_his_surveys.py
2026-03-28 14:03:56 +03:00

190 lines
7.4 KiB
Python

"""
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!"))