diff --git a/care/facility/migrations/0477_historicalpatientregistration_migrated_emr_patient_id_and_more.py b/care/facility/migrations/0477_historicalpatientregistration_migrated_emr_patient_id_and_more.py new file mode 100644 index 0000000000..90bcba0900 --- /dev/null +++ b/care/facility/migrations/0477_historicalpatientregistration_migrated_emr_patient_id_and_more.py @@ -0,0 +1,280 @@ +# Generated by Django 5.1.4 on 2025-01-14 09:20 + +import django.db.models.deletion +from django.db import migrations, models + +import logging +from django.core.paginator import Paginator + +logger = logging.getLogger(__name__) + + +MIGRARION_ID = 413370 +COUNTRY = "India" + +def _get_org(Organization, obj): + if obj.ward_id: + return Organization.objects.get(name=obj.ward.name) + elif obj.local_body_id: + return Organization.objects.get(name=obj.local_body.name) + elif obj.district_id: + return Organization.objects.get(name=obj.district.name) + elif obj.state_id: + return Organization.objects.get(name=obj.state.name) + return None + +def migrate_organizations(apps, schema_editor): + Organization = apps.get_model("emr", "Organization") + State = apps.get_model("users", "State") + District = apps.get_model("users", "District") + LocalBody = apps.get_model("users", "LocalBody") + Ward = apps.get_model("users", "Ward") + + logger.debug("Migrating Organization") + for state_obj in State.objects.all(): + state_org = Organization.objects.create( + name=state_obj.name, + org_type="govt", + system_generated=True, + metadata={ + "country": COUNTRY, + "govt_org_type": "state", + "govt_org_children_type": "district", + }, + meta= {"migration_id": MIGRARION_ID}, + ) + logger.debug(f"Created State: {state_org.name=}") + for district_obj in District.objects.filter(state=state_obj): + district_org = Organization.objects.create( + name=district_obj.name, + root_org=state_org, + parent=state_org, + org_type="govt", + system_generated=True, + metadata={ + "country": COUNTRY, + "govt_org_type": "district", + "govt_org_children_type": "local_body", + }, + meta= {"migration_id": MIGRARION_ID}, + ) + logger.debug(f"Created District: {state_org.name=}, {district_org.name=}") + for local_body_obj in LocalBody.objects.filter(district=district_obj): + local_body_org = Organization.objects.create( + name=local_body_obj.name, + root_org=state_org, + parent=district_org, + org_type="govt", + system_generated=True, + metadata={ + #TODO: add old types + "country": COUNTRY, + "govt_org_type": "local_body", + "govt_org_children_type": "ward", + }, + meta= {"migration_id": MIGRARION_ID}, + ) + logger.debug(f"Created Local Body: {state_org.name=}, \ + {district_org.name=}, {local_body_org.name=}") + for ward_obj in Ward.objects.filter(local_body=local_body_obj): + ward_org = Organization.objects.create( + name=ward_obj.name, + root_org=state_org, + parent=local_body_org, + org_type="govt", + system_generated=True, + metadata={ + #TODO: add ward numbers + "country": COUNTRY, + "govt_org_type": "ward", + }, + meta= {"migration_id": MIGRARION_ID}, + ) + logger.debug(f"Created Ward: {state_org.name=}, \ + {district_org.name=}, {local_body_org.name=}, \ + {ward_org.name=}") + + +def reverse_migrate_organizations(apps, schema_editor): + logger.debug("Reversing Migration Organization") + schema_editor.execute("DELETE FROM emr_organization WHERE meta->>'migration_id' = %s", [MIGRARION_ID]) + + +def migrate_users(apps, schema_editor): + User = apps.get_model("users", "User") + Organization = apps.get_model("emr", "Organization") + logger.debug("Migrating Users") + bulk_update = [] + for user in User.objects.all().select_related("state", "district", "local_body", "ward"): + if user.geo_organization: + continue + geo_org = _get_org(Organization, user) + if not geo_org: + continue + user.geo_organization = geo_org + logger.debug(f"User: {user.name=}, {geo_org.name=}") + bulk_update.append(user) + User.objects.bulk_update(bulk_update, ["geo_organization"]) + +def reverse_migrate_users(apps, schema_editor): + User = apps.get_model("users", "User") + User.objects.filter( + geo_organization__meta__migration_id=MIGRARION_ID + ).update( + geo_organization=None + ) + + +def migrate_facilities(apps, schema_editor): + Facility = apps.get_model("facility", "Facility") + Organization = apps.get_model("emr", "Organization") + bulk_update = [] + for facility in Facility.objects.all().select_related("state", "district", "local_body", "ward"): + if facility.geo_organization: + continue + geo_org = _get_org(Organization, facility) + if not geo_org: + continue + facility.geo_organization = geo_org + #TODO: create facility organization + bulk_update.append(facility) + Facility.objects.bulk_update(bulk_update, ["geo_organization"]) + +def reverse_migrate_facilities(apps, schema_editor): + Facility = apps.get_model("facility", "Facility") + Facility.objects.filter( + geo_organization__meta__migration_id=MIGRARION_ID + ).update( + geo_organization=None + ) + +def migrate_patient_registrations(apps, schema_editor): + PatientRegistration = apps.get_model("facility", "PatientRegistration") + Patient = apps.get_model("emr", "Patient") + Organization = apps.get_model("emr", "Organization") + + gender_map = { + 1: "male", + 2: "female", + 3: "non_binary", + } + blood_group_map = { + "A+": "A_positive", + "A-": "A_negative", + "B+": "B_positive", + "B-": "B_negative", + "AB+": "AB_positive", + "AB-": "AB_negative", + "O+": "O_positive", + "O-": "O_negative", + "UNK": "unknown", + } + + logger.debug("Migrating Patient Registrations") + patient_registrations = PatientRegistration.objects.all() + paginator = Paginator(patient_registrations, 2000) + + for page_number in paginator.page_range: + page = paginator.page(page_number) + bulk = [] + for patient_registration in page.object_list: + patient = Patient.objects.create( + name=patient_registration.name, + gender=gender_map.get(patient_registration.gender, ""), + phone_number=patient_registration.phone_number, + emergency_phone_number=patient_registration.emergency_phone_number, + address=patient_registration.address, + permanent_address=patient_registration.permanent_address, + pincode=patient_registration.pincode, + date_of_birth=patient_registration.date_of_birth, + year_of_birth=patient_registration.year_of_birth, + deceased_datetime=patient_registration.death_datetime, + blood_group=blood_group_map.get(patient_registration.blood_group, ""), + geo_organization=_get_org(Organization, patient_registration), + meta={ + "migration_id": MIGRARION_ID, + }, + created_by=patient_registration.created_by, + updated_by=patient_registration.updated_by, + created_at=patient_registration.created_at, + updated_at=patient_registration.updated_at, + ) + patient_registration.migrated_emr_patient_id = patient.id + bulk.append(patient_registration) + logger.debug(f"Created Patient: {patient.name=}") + PatientRegistration.objects.bulk_update(bulk, ["migrated_emr_patient_id"]) + +def reverse_migrate_patient_registrations(apps, schema_editor): + PatientRegistration = apps.get_model("facility", "PatientRegistration") + PatientRegistration.objects.update(migrated_emr_patient_id=None) + schema_editor.execute("DELETE FROM emr_patient WHERE meta->>'migration_id' = %s", [MIGRARION_ID]) + + +def migrate_consultations(apps, schema_editor): + PatientConsultation = apps.get_model("facility", "PatientConsultation") + Encounter = apps.get_model("emr", "Encounter") + + paginator = Paginator(PatientConsultation.objects.all(), 2000) + + for page_number in paginator.page_range: + page = paginator.page(page_number) + bulk = [] + for patient_consultation in page.object_list: + encounter = Encounter.objects.create( + meta={ + "migration_id": MIGRARION_ID, + }, + created_by=patient_consultation.created_by, + updated_by=patient_consultation.updated_by, + created_at=patient_consultation.created_at, + updated_at=patient_consultation.updated_at, + ) + patient_consultation.migrated_emr_encounter_id = encounter.id + bulk.append(patient_consultation) + logger.debug(f"Created Encounter: {encounter.id=}") + PatientConsultation.objects.bulk_update(bulk, ["migrated_emr_encounter_id"]) + + + +def reverse_migrate_consultations(apps, schema_editor): + pass + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('facility', '0476_facility_default_internal_organization_and_more'), + ] + + operations = [ + migrations.DeleteModel( + name='HistoricalPatientRegistration', + ), + migrations.AddField( + model_name='patientconsultation', + name='migrated_emr_encounter_id', + field=models.BigIntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='patientregistration', + name='migrated_emr_patient_id', + field=models.BigIntegerField(blank=True, null=True), + ), + migrations.CreateModel( + name='MigrationTracking', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('old_model_obj_id', models.BigIntegerField()), + ('new_model_obj_id', models.BigIntegerField()), + ('field', models.CharField(max_length=255)), + ('data', models.TimeField()), + ('new_model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='new_model', to='contenttypes.contenttype')), + ('old_model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='old_model', to='contenttypes.contenttype')), + ], + ), + migrations.RunPython(migrate_organizations, reverse_migrate_organizations), + migrations.RunPython(migrate_users, reverse_migrate_users), + migrations.RunPython(migrate_facilities, reverse_migrate_facilities), + migrations.RunPython(migrate_patient_registrations, reverse_migrate_patient_registrations), + migrations.RunPython(migrate_consultations, reverse_migrate_consultations), + ] diff --git a/care/facility/models/__init__.py b/care/facility/models/__init__.py index 5bae44b627..9858acdc31 100644 --- a/care/facility/models/__init__.py +++ b/care/facility/models/__init__.py @@ -23,3 +23,4 @@ from .shifting import * # noqa from .stats import * # noqa from .notification import * # noqa +from .migration_tracking import * # noqa diff --git a/care/facility/models/migration_tracking.py b/care/facility/models/migration_tracking.py new file mode 100644 index 0000000000..8eeee37ebb --- /dev/null +++ b/care/facility/models/migration_tracking.py @@ -0,0 +1,22 @@ +from django.contrib.contenttypes.models import ContentType +from django.db import models + + +class MigrationTracking(models.Model): + """ + Model to track the migrations that have been run on the database. + """ + + old_model = models.ForeignKey( + ContentType, on_delete=models.CASCADE, related_name="old_model" + ) + old_model_obj_id = models.BigIntegerField() + new_model = models.ForeignKey( + ContentType, on_delete=models.CASCADE, related_name="new_model" + ) + new_model_obj_id = models.BigIntegerField() + field = models.CharField(max_length=255) + data = models.TimeField() + + def __str__(self): + return f"{self.old_model}({self.old_model_obj_id}) -> {self.new_model} : {self.field}" diff --git a/care/facility/models/patient.py b/care/facility/models/patient.py index ccaab1ff96..59bedb86d2 100644 --- a/care/facility/models/patient.py +++ b/care/facility/models/patient.py @@ -11,7 +11,6 @@ from django.template.defaultfilters import pluralize from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from simple_history.models import HistoricalRecords from care.facility.models import ( DISEASE_CHOICES, @@ -447,7 +446,9 @@ class TestTypeEnum(enum.Enum): organization_cache = ArrayField(models.IntegerField(), default=list) - history = HistoricalRecords(excluded_fields=["meta_info"]) + migrated_emr_patient_id = models.BigIntegerField(null=True, blank=True) + + # history = HistoricalRecords(excluded_fields=["meta_info"]) objects = BaseManager() diff --git a/care/facility/models/patient_consultation.py b/care/facility/models/patient_consultation.py index 755a323784..7282030d4c 100644 --- a/care/facility/models/patient_consultation.py +++ b/care/facility/models/patient_consultation.py @@ -223,6 +223,8 @@ class PatientConsultation(PatientBaseModel, ConsultationRelatedPermissionMixin): default=list, ) + migrated_emr_encounter_id = models.BigIntegerField(null=True, blank=True) + def get_related_consultation(self): return self