From b36e1b313e18671fc26498e7e408115d0e0e9c93 Mon Sep 17 00:00:00 2001 From: euanmillar Date: Tue, 26 Aug 2025 18:16:47 +0530 Subject: [PATCH 01/20] Added mappings for Somalia --- .../countryData/countryMappings.ts | 28 +- .../countryData/countryResolvers.ts | 60 +++- v1-to-v2-data-migration/get-field-diff.ipynb | 283 +++++++++++++++++- 3 files changed, 360 insertions(+), 11 deletions(-) diff --git a/v1-to-v2-data-migration/countryData/countryMappings.ts b/v1-to-v2-data-migration/countryData/countryMappings.ts index d56484d..64f4c76 100644 --- a/v1-to-v2-data-migration/countryData/countryMappings.ts +++ b/v1-to-v2-data-migration/countryData/countryMappings.ts @@ -6,4 +6,30 @@ {v1Field.customQuestionMappingId}: {v2Field.id} */ -export const COUNTRY_FIELD_MAPPINGS = {} +export const COUNTRY_FIELD_MAPPINGS = { + 'birth.child-view-group.iD': 'child.nid', + 'birth.child-view-group.countryPrimaryChild': 'child.address', + 'birth.child-view-group.statePrimaryChild': 'child.address', + 'birth.child-view-group.districtPrimaryChild': 'child.address', + 'birth.child-view-group.cityPrimaryChild': 'child.address', + 'birth.child-view-group.addressLine1PrimaryChild': 'child.address', + 'birth.child-view-group.addressLine2PrimaryChild': 'child.address', + 'birth.child-view-group.addressLine3PrimaryChild': 'child.address', + 'birth.child-view-group.postalCodePrimaryChild': 'child.address', + 'birth.child-view-group.internationalCityPrimaryChild': 'child.address', + 'birth.child-view-group.birthAttendantName': 'child.attendantName', + 'birth.child-view-group.birthAttendantId': 'child.attedantAtBirthId', + 'birth.informant-view-group.informantID': 'informant.nid', + 'birth.mother-view-group.iD': 'mother.nid', + 'birth.father-view-group.iD': 'father.nid', + 'birth.documents-view-group.uploadDocOther': 'documents.proofOther', + 'death.deceased-view-group.placeOfBirth': 'deceased.placeOfBirth', + 'death.deceased-view-group.birthRegNo': 'deceased.brn', + 'death.deceased-view-group.deceasedID': 'deceased.nid', + 'death.deceased-view-group.occupation': 'deceased.occupation', + 'death.death-event-details.timeOfDeath': 'eventDetails.timeOfDeath', + 'death.informant-view-group.informantID': 'informant.nid', + 'death.informant-view-group.informantType': 'informant.informantType', + 'death.documents-view-group.uploadDocForInformant': + 'documents.proofOfInformant', +} diff --git a/v1-to-v2-data-migration/countryData/countryResolvers.ts b/v1-to-v2-data-migration/countryData/countryResolvers.ts index 22e1d1b..8026cb5 100644 --- a/v1-to-v2-data-migration/countryData/countryResolvers.ts +++ b/v1-to-v2-data-migration/countryData/countryResolvers.ts @@ -1 +1,59 @@ -export const countryResolver = {} +import { getCustomField, getIdentifier } from "./resolverUtils.ts"; + +function convertChildAddress(data, address) { + if (!address) { + return null; + } + + const country = getCustomField( + data, + "birth.child.child-view-group.countryPrimaryChild" + ); + const international = country !== "FAR"; //This doesn't work for other countries + if (international) { + return { + addressType: "INTERNATIONAL", + country, + cityOrTown: getCustomField( + data, + "birth.child-view-group.internationalCityPrimaryChild" + ), + }; + } + return { + addressType: "DOMESTIC", + country, + state: getCustomField(data, "birth.child-view-group.statePrimaryChild"), + district: getCustomField( + data, + "birth.child-view-group.districtPrimaryChild" + ), + cityOrTown: getCustomField(data, "birth.child-view-group.cityPrimaryChild"), + addressLine1: getCustomField( + data, + "birth.child-view-group.addressLine1PrimaryChild" + ), + addressLine2: getCustomField( + data, + "birth.child-view-group.addressLine2PrimaryChild" + ), + addressLine3: getCustomField( + data, + "birth.child-view-group.addressLine3PrimaryChild" + ), + postcodeOrZip: getCustomField( + data, + "birth.child-view-group.postalCodePrimaryChild" + ), + }; +} + +export const countryResolver = { + "child.nid": (data) => getIdentifier(data.child, "NATIONAL_ID"), + "child.address": (data) => + convertChildAddress(data, data.eventLocation.address), + "child.attendantName": (data) => + getCustomField(data, "birth.child.child-view-group.birthAttendantName"), + "child.attedantAtBirthId": (data) => + getCustomField(data, "birth.child.child-view-group.birthAttendantId"), +}; diff --git a/v1-to-v2-data-migration/get-field-diff.ipynb b/v1-to-v2-data-migration/get-field-diff.ipynb index 09bdc1d..07b6bbe 100644 --- a/v1-to-v2-data-migration/get-field-diff.ipynb +++ b/v1-to-v2-data-migration/get-field-diff.ipynb @@ -23,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "628ffaa7", "metadata": { "execution": { @@ -33,17 +33,28 @@ "shell.execute_reply": "2025-08-11T16:16:03.160572Z" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[32m\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyZS1iaXJ0aCIsInJlY29yZC5kZWNsYXJlLWRlYXRoIiwicmVjb3JkLmRlY2xhcmUtbWFycmlhZ2UiLCJyZWNvcmQuZGVjbGFyYXRpb24tZWRpdCIsInJlY29yZC5kZWNsYXJhdGlvbi1zdWJtaXQtZm9yLXVwZGF0ZXMiLCJyZWNvcmQucmV2aWV3LWR1cGxpY2F0ZXMiLCJyZWNvcmQuZGVjbGFyYXRpb24tYXJjaGl2ZSIsInJlY29yZC5kZWNsYXJhdGlvbi1yZWluc3RhdGUiLCJyZWNvcmQucmVnaXN0ZXIiLCJyZWNvcmQucmVnaXN0cmF0aW9uLWNvcnJlY3QiLCJyZWNvcmQuZGVjbGFyYXRpb24tcHJpbnQtc3VwcG9ydGluZy1kb2N1bWVudHMiLCJyZWNvcmQuZXhwb3J0LXJlY29yZHMiLCJyZWNvcmQudW5hc3NpZ24tb3RoZXJzIiwicmVjb3JkLnJlZ2lzdHJhdGlvbi1wcmludCZpc3N1ZS1jZXJ0aWZpZWQtY29waWVzIiwicmVjb3JkLmNvbmZpcm0tcmVnaXN0cmF0aW9uIiwicmVjb3JkLnJlamVjdC1yZWdpc3RyYXRpb24iLCJwZXJmb3JtYW5jZS5yZWFkIiwicGVyZm9ybWFuY2UucmVhZC1kYXNoYm9hcmRzIiwicGVyZm9ybWFuY2Uudml0YWwtc3RhdGlzdGljcy1leHBvcnQiLCJwcm9maWxlLmVsZWN0cm9uaWMtc2lnbmF0dXJlIiwib3JnYW5pc2F0aW9uLnJlYWQtbG9jYXRpb25zOm15LW9mZmljZSIsInNlYXJjaC5iaXJ0aCIsInNlYXJjaC5kZWF0aCIsInNlYXJjaC5tYXJyaWFnZSIsInVzZXIucmVhZDpteS1vZmZpY2UiLCJzZWFyY2hbZXZlbnQ9djIuYmlydGgsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9djIuZGVhdGgsYWNjZXNzPWFsbF0iLCJ3b3JrcXVldWVbaWQ9YXNzaWduZWQtdG8teW91fHJlY2VudHxyZXF1aXJlcy1jb21wbGV0aW9ufHJlcXVpcmVzLXVwZGF0ZXMtb2ZmaWNlfGluLXJldmlldy1hbGx8aW4tZXh0ZXJuYWwtdmFsaWRhdGlvbnxyZWFkeS10by1wcmludHxyZWFkeS10by1pc3N1ZV0iLCJkZW1vIl0sInVzZXJUeXBlIjoidXNlciIsImlhdCI6MTc1NjIxMTc5NiwiZXhwIjoxNzU2ODE2NTk2LCJhdWQiOlsib3BlbmNydnM6YXV0aC11c2VyIiwib3BlbmNydnM6dXNlci1tZ250LXVzZXIiLCJvcGVuY3J2czpoZWFydGgtdXNlciIsIm9wZW5jcnZzOmdhdGV3YXktdXNlciIsIm9wZW5jcnZzOm5vdGlmaWNhdGlvbi11c2VyIiwib3BlbmNydnM6d29ya2Zsb3ctdXNlciIsIm9wZW5jcnZzOnNlYXJjaC11c2VyIiwib3BlbmNydnM6bWV0cmljcy11c2VyIiwib3BlbmNydnM6Y291bnRyeWNvbmZpZy11c2VyIiwib3BlbmNydnM6d2ViaG9va3MtdXNlciIsIm9wZW5jcnZzOmNvbmZpZy11c2VyIiwib3BlbmNydnM6ZG9jdW1lbnRzLXVzZXIiXSwiaXNzIjoib3BlbmNydnM6YXV0aC1zZXJ2aWNlIiwic3ViIjoiNjhhZDgzZjVjNmU5M2QyOTk2M2YzZjBhIn0.yIrOhJp4wPMCZ1Hr062WVKLmfVKg3HTTuB0Qg6fKpfAcK8MiEEj0VSQwppm9jDPQoG6SZFsACmS9DHKGy4rv83evLcuhNO3ir0Y8Xlmc8TuO3kuwUwrxzXYHhL1O9Nxkz7XL9kWFgZ6jvAue1GrhrZmASsxL97nlTqj33hB0pBTudobzUlvxKJg49HMd9txt8vM1xLYJnykHDWK6z9Ak2g3Mxai4j3ib5raN3dL3U30_0aoLHegHHretOjP1ASbhet-ImN7tgwozdlWfz-GEHmbVAgWJ6_T2GxEWm9uqE1Cvw1SJvmBAKabHHYyxxjUccxqxlV3LbrTWZeNsx4jH5w\"\u001b[39m" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import { authenticate } from './helpers/authentication.ts'\n", "\n", - "const token = await authenticate( 'k.mweene', 'test')\n", + "const token = await authenticate(GATEWAY, 's.faisal', 'test')\n", "token\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "7ffd8b2c", "metadata": { "execution": { @@ -67,7 +78,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "b4367b4e", "metadata": { "execution": { @@ -77,7 +88,111 @@ "shell.execute_reply": "2025-08-11T16:16:03.287888Z" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[\n", + " \u001b[32m\"birth.child-view-group.firstNamesEng\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.familyNameEng\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.childBirthDate\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.gender\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.iD\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.placeOfBirth\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.birthLocation\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.countryPlaceofbirth\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.statePlaceofbirth\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.districtPlaceofbirth\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.cityPlaceofbirth\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.addressLine1Placeofbirth\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.addressLine2Placeofbirth\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.addressLine3Placeofbirth\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.postalCodePlaceofbirth\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.internationalCityPlaceofbirth\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.countryPrimaryChild\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.statePrimaryChild\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.districtPrimaryChild\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.cityPrimaryChild\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.addressLine1PrimaryChild\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.addressLine2PrimaryChild\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.addressLine3PrimaryChild\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.postalCodePrimaryChild\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.internationalCityPrimaryChild\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.attendantAtBirth\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.birthAttendantName\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.birthAttendantId\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.birthType\"\u001b[39m,\n", + " \u001b[32m\"birth.child-view-group.weightAtBirth\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.informantType\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.otherInformantType\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.firstNamesEng\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.familyNameEng\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.informantBirthDate\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.exactDateOfBirthUnknown\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.ageOfIndividualInYears\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.nationality\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.informantID\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.countryPrimaryInformant\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.statePrimaryInformant\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.districtPrimaryInformant\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.cityPrimaryInformant\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.addressLine1PrimaryInformant\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.addressLine2PrimaryInformant\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.addressLine3PrimaryInformant\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.postalCodePrimaryInformant\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.internationalCityPrimaryInformant\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.registrationPhone\"\u001b[39m,\n", + " \u001b[32m\"birth.informant-view-group.registrationEmail\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.detailsExist\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.reasonNotApplying\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.firstNamesEng\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.familyNameEng\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.motherBirthDate\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.exactDateOfBirthUnknown\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.ageOfIndividualInYears\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.nationality\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.iD\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.countryPrimaryMother\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.statePrimaryMother\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.districtPrimaryMother\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.cityPrimaryMother\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.addressLine1PrimaryMother\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.addressLine2PrimaryMother\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.addressLine3PrimaryMother\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.postalCodePrimaryMother\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.internationalCityPrimaryMother\"\u001b[39m,\n", + " \u001b[32m\"birth.mother-view-group.multipleBirth\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.detailsExist\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.reasonNotApplying\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.firstNamesEng\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.familyNameEng\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.fatherBirthDate\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.exactDateOfBirthUnknown\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.ageOfIndividualInYears\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.nationality\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.iD\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.countryPrimaryFather\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.statePrimaryFather\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.districtPrimaryFather\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.cityPrimaryFather\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.addressLine1PrimaryFather\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.addressLine2PrimaryFather\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.addressLine3PrimaryFather\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.postalCodePrimaryFather\"\u001b[39m,\n", + " \u001b[32m\"birth.father-view-group.internationalCityPrimaryFather\"\u001b[39m,\n", + " \u001b[32m\"birth.documents-view-group.uploadDocForChildDOB\"\u001b[39m,\n", + " \u001b[32m\"birth.documents-view-group.uploadDocForMother\"\u001b[39m,\n", + " \u001b[32m\"birth.documents-view-group.uploadDocForFather\"\u001b[39m,\n", + " \u001b[32m\"birth.documents-view-group.uploadDocForInformant\"\u001b[39m,\n", + " \u001b[32m\"birth.documents-view-group.uploadDocOther\"\u001b[39m\n", + "]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import { extractFieldType, extractFormFields } from './helpers/utils.ts'\n", "\n", @@ -121,7 +236,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "1bfd59ab", "metadata": { "execution": { @@ -222,7 +337,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "475c7d11", "metadata": { "execution": { @@ -232,7 +347,157 @@ "shell.execute_reply": "2025-08-11T16:16:03.314888Z" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unmapped V1 form fields:\n", + "\n", + "Birth:\n", + "None \u001b[32m✔\u001b[0m\n", + "Death:\n", + "None \u001b[32m✔\u001b[0m\n", + "\n", + "V1 form fields mapped to a V2 field that does not exist:\n", + "Birth:\n", + "None \u001b[32m✔\u001b[0m\n", + "Death:\n", + "None \u001b[32m✔\u001b[0m\n", + "\n", + "V1 form fields mapped but V2 field does not have a resolver:\n", + "Birth:\n", + "┌───────┬────────────────────────────────────────────────────────┬───────────────────────────┐\n", + "│ (idx) │ V1 Field │ V2 Field │\n", + "├───────┼────────────────────────────────────────────────────────┼───────────────────────────┤\n", + "│ 0 │ \"birth.child-view-group.iD\" │ \"child.nid\" │\n", + "│ 1 │ \"birth.child-view-group.countryPrimaryChild\" │ \"child.address\" │\n", + "│ 2 │ \"birth.child-view-group.statePrimaryChild\" │ \"child.address\" │\n", + "│ 3 │ \"birth.child-view-group.districtPrimaryChild\" │ \"child.address\" │\n", + "│ 4 │ \"birth.child-view-group.cityPrimaryChild\" │ \"child.address\" │\n", + "│ 5 │ \"birth.child-view-group.addressLine1PrimaryChild\" │ \"child.address\" │\n", + "│ 6 │ \"birth.child-view-group.addressLine2PrimaryChild\" │ \"child.address\" │\n", + "│ 7 │ \"birth.child-view-group.addressLine3PrimaryChild\" │ \"child.address\" │\n", + "│ 8 │ \"birth.child-view-group.postalCodePrimaryChild\" │ \"child.address\" │\n", + "│ 9 │ \"birth.child-view-group.internationalCityPrimaryChild\" │ \"child.address\" │\n", + "│ 10 │ \"birth.child-view-group.birthAttendantName\" │ \"child.attendantName\" │\n", + "│ 11 │ \"birth.child-view-group.birthAttendantId\" │ \"child.attedantAtBirthId\" │\n", + "└───────┴────────────────────────────────────────────────────────┴───────────────────────────┘\n", + "Death:\n", + "┌───────┬────────────────────────────────────────────┬────────────────────────────┐\n", + "│ (idx) │ V1 Field │ V2 Field │\n", + "├───────┼────────────────────────────────────────────┼────────────────────────────┤\n", + "│ 0 │ \"death.deceased-view-group.placeOfBirth\" │ \"deceased.placeOfBirth\" │\n", + "│ 1 │ \"death.deceased-view-group.occupation\" │ \"deceased.occupation\" │\n", + "│ 2 │ \"death.death-event-details.timeOfDeath\" │ \"eventDetails.timeOfDeath\" │\n", + "│ 3 │ \"death.informant-view-group.informantType\" │ \"informant.informantType\" │\n", + "└───────┴────────────────────────────────────────────┴────────────────────────────┘\n", + "\n", + "Additional V2 form fields not mapped:\n", + "Birth:\n", + "┌───────┬───────────────────────────────────────────────────────────────┐\n", + "│ (idx) │ Values │\n", + "├───────┼───────────────────────────────────────────────────────────────┤\n", + "│ 0 │ \"informant.contact\" │\n", + "│ 1 │ \"review.comment\" │\n", + "│ 2 │ \"collector.requesterId\" │\n", + "│ 3 │ \"collector.OTHER.idType\" │\n", + "│ 4 │ \"collector.PASSPORT.details\" │\n", + "│ 5 │ \"collector.nid\" │\n", + "│ 6 │ \"collector.DRIVING_LICENSE.details\" │\n", + "│ 7 │ \"collector.brn\" │\n", + "│ 8 │ \"collector.REFUGEE_NUMBER.details\" │\n", + "│ 9 │ \"collector.ALIEN_NUMBER.details\" │\n", + "│ 10 │ \"collector.OTHER.idTypeOther\" │\n", + "│ 11 │ \"collector.OTHER.idNumberOther\" │\n", + "│ 12 │ \"collector.OTHER.name\" │\n", + "│ 13 │ \"collector.OTHER.relationshipToChild\" │\n", + "│ 14 │ \"collector.OTHER.signedAffidavit\" │\n", + "│ 15 │ \"collector.identity.verify.data.mother\" │\n", + "│ 16 │ \"collector.identity.verify.data.father\" │\n", + "│ 17 │ \"collector.identity.verify.data.other\" │\n", + "│ 18 │ \"collector.collect.payment.data.afterLateRegistrationTarget\" │\n", + "│ 19 │ \"collector.collect.payment.data.inBetweenRegistrationTargets\" │\n", + "│ 20 │ \"collector.collect.payment.data.beforeRegistrationTarget\" │\n", + "│ 21 │ \"requester.type\" │\n", + "│ 22 │ \"requester.idType\" │\n", + "│ 23 │ \"requester.nid\" │\n", + "│ 24 │ \"requester.passport\" │\n", + "│ 25 │ \"requester.brn\" │\n", + "│ 26 │ \"requester.name\" │\n", + "│ 27 │ \"requester.relationship\" │\n", + "│ 28 │ \"reason.option\" │\n", + "│ 29 │ \"reason.other\" │\n", + "│ 30 │ \"requester.identity.verify.data\" │\n", + "│ 31 │ \"documents.supportingDocs\" │\n", + "│ 32 │ \"fees.amount\" │\n", + "│ 33 │ \"child.birthLocation.privateHome\" │\n", + "│ 34 │ \"child.birthLocation.other\" │\n", + "│ 35 │ \"father.addressSameAs\" │\n", + "└───────┴───────────────────────────────────────────────────────────────┘\n", + "Death:\n", + "┌───────┬───────────────────────────────────────────────────────────┐\n", + "│ (idx) │ Values │\n", + "├───────┼───────────────────────────────────────────────────────────┤\n", + "│ 0 │ \"informant.contact\" │\n", + "│ 1 │ \"review.comment\" │\n", + "│ 2 │ \"collector.requesterId\" │\n", + "│ 3 │ \"collector.OTHER.idType\" │\n", + "│ 4 │ \"collector.PASSPORT.details\" │\n", + "│ 5 │ \"collector.nid\" │\n", + "│ 6 │ \"collector.DRIVING_LICENSE.details\" │\n", + "│ 7 │ \"collector.brn\" │\n", + "│ 8 │ \"collector.REFUGEE_NUMBER.details\" │\n", + "│ 9 │ \"collector.ALIEN_NUMBER.details\" │\n", + "│ 10 │ \"collector.OTHER.idTypeOther\" │\n", + "│ 11 │ \"collector.OTHER.idNumberOther\" │\n", + "│ 12 │ \"collector.OTHER.name\" │\n", + "│ 13 │ \"collector.OTHER.relationshipToDeceased\" │\n", + "│ 14 │ \"collector.OTHER.signedAffidavit\" │\n", + "│ 15 │ \"collector.identity.verify.data.spouse\" │\n", + "│ 16 │ \"collector.identity.verify.data.other\" │\n", + "│ 17 │ \"collector.collect.payment.data.afterRegistrationTarget\" │\n", + "│ 18 │ \"collector.collect.payment.data.beforeRegistrationTarget\" │\n", + "│ 19 │ \"requester.type\" │\n", + "│ 20 │ \"requester.idType\" │\n", + "│ 21 │ \"requester.nid\" │\n", + "│ 22 │ \"requester.passport\" │\n", + "│ 23 │ \"requester.brn\" │\n", + "│ 24 │ \"requester.name\" │\n", + "│ 25 │ \"requester.relationship\" │\n", + "│ 26 │ \"reason.option\" │\n", + "│ 27 │ \"reason.other\" │\n", + "│ 28 │ \"requester.identity.verify.data\" │\n", + "│ 29 │ \"documents.supportingDocs\" │\n", + "│ 30 │ \"fees.amount\" │\n", + "│ 31 │ \"informant.addressSameAs\" │\n", + "│ 32 │ \"spouse.detailsNotAvailable\" │\n", + "│ 33 │ \"spouse.reason\" │\n", + "│ 34 │ \"spouse.name\" │\n", + "│ 35 │ \"spouse.dob\" │\n", + "│ 36 │ \"spouse.dobUnknown\" │\n", + "│ 37 │ \"spouse.age\" │\n", + "│ 38 │ \"spouse.nationality\" │\n", + "│ 39 │ \"spouse.idType\" │\n", + "│ 40 │ \"spouse.nid\" │\n", + "│ 41 │ \"spouse.passport\" │\n", + "│ 42 │ \"spouse.brn\" │\n", + "│ 43 │ \"spouse.addressSameAs\" │\n", + "│ 44 │ \"spouse.address\" │\n", + "└───────┴───────────────────────────────────────────────────────────┘\n" + ] + }, + { + "ename": "Error", + "evalue": "Migration may not proceed. Please fix errors", + "output_type": "error", + "traceback": [ + "Stack trace:", + "Error: Migration may not proceed. Please fix errors", + " at :45:9" + ] + } + ], "source": [ "const render = (list) => {\n", " if (list.length) {\n", From 8f4514e48957f9a62a671c2bb0ca54531fecbb7c Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Fri, 3 Oct 2025 17:00:56 +0200 Subject: [PATCH 02/20] Update to use new field formats --- .gitignore | 2 + .../countryData/addressMappings.ts | 106 +++++-- .../countryData/addressResolver.ts | 37 ++- .../countryData/countryMappings.ts | 41 +-- .../countryData/countryResolvers.ts | 109 ++++--- v1-to-v2-data-migration/get-field-diff.ipynb | 292 +----------------- .../helpers/authentication.ts | 7 + .../helpers/defaultResolvers.ts | 2 +- v1-to-v2-data-migration/helpers/transform.ts | 27 +- v1-to-v2-data-migration/helpers/types.ts | 24 +- v1-to-v2-data-migration/helpers/vars.ts | 1 + v1-to-v2-data-migration/migrate.ipynb | 12 +- 12 files changed, 232 insertions(+), 428 deletions(-) diff --git a/.gitignore b/.gitignore index 4b7214e..a902b5b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ test-results/ .vscode **/tmp/* */countryData/v1Forms.json +*/countryData/v2EventFields.json +*/countryData/v1FormFields.json .env \ No newline at end of file diff --git a/v1-to-v2-data-migration/countryData/addressMappings.ts b/v1-to-v2-data-migration/countryData/addressMappings.ts index cb8d940..579f74c 100644 --- a/v1-to-v2-data-migration/countryData/addressMappings.ts +++ b/v1-to-v2-data-migration/countryData/addressMappings.ts @@ -1,77 +1,129 @@ -type AddressConfigFunction = (data: string) => Record +import { Address } from './addressResolver.ts' + +type AddressConfigFunction = (data: string) => Record export const ADDRESS_MAPPINGS: Record = { // Birth - Child Place of Birth 'birth.child.countryPlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { country: data }, + 'child.birthLocation.privateHome': { country: data }, }), 'birth.child.statePlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { state: data }, + 'child.birthLocation.privateHome': { + /* ignore */ + }, }), 'birth.child.districtPlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { administrativeArea: data }, + 'child.birthLocation.privateHome': { administrativeArea: data }, }), 'birth.child.cityPlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { streetLevelDetails: { town: data } }, + 'child.birthLocation.privateHome': { streetLevelDetails: { city: data } }, }), 'birth.child.addressLine1Placeofbirth': (data: string) => ({ - 'child.address.privateHome': { + 'child.birthLocation.privateHome': { streetLevelDetails: { residentialArea: data }, }, }), 'birth.child.addressLine2Placeofbirth': (data: string) => ({ - 'child.address.privateHome': { streetLevelDetails: { street: data } }, + 'child.birthLocation.privateHome': { streetLevelDetails: { street: data } }, }), 'birth.child.addressLine3Placeofbirth': (data: string) => ({ - 'child.address.privateHome': { streetLevelDetails: { number: data } }, + 'child.birthLocation.privateHome': { streetLevelDetails: { number: data } }, }), 'birth.child.postalCodePlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { streetLevelDetails: { zipCode: data } }, + 'child.birthLocation.privateHome': { + streetLevelDetails: { zipCode: data }, + }, }), 'birth.child.internationalStatePlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { + 'child.birthLocation.privateHome': { streetLevelDetails: { state: data }, }, }), 'birth.child.internationalDistrictPlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { streetLevelDetails: { district2: data } }, + 'child.birthLocation.privateHome': { + streetLevelDetails: { district2: data }, + }, }), 'birth.child.internationalCityPlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { streetLevelDetails: { cityOrTown: data } }, + 'child.birthLocation.privateHome': { + streetLevelDetails: { cityOrTown: data }, + }, }), 'birth.child.internationalAddressLine1Placeofbirth': (data: string) => ({ - 'child.address.privateHome': { streetLevelDetails: { addressLine1: data } }, + 'child.birthLocation.privateHome': { + streetLevelDetails: { addressLine1: data }, + }, }), 'birth.child.internationalAddressLine2Placeofbirth': (data: string) => ({ - 'child.address.privateHome': { streetLevelDetails: { addressLine2: data } }, + 'child.birthLocation.privateHome': { + streetLevelDetails: { addressLine2: data }, + }, }), 'birth.child.internationalAddressLine3Placeofbirth': (data: string) => ({ - 'child.address.privateHome': { streetLevelDetails: { addressLine3: data } }, + 'child.birthLocation.privateHome': { + streetLevelDetails: { addressLine3: data }, + }, }), 'birth.child.internationalPostalCodePlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { + 'child.birthLocation.privateHome': { streetLevelDetails: { postcodeOrZip: data }, }, }), // Birth - Child Place of Birth (Urban/Rural Options) 'birth.child.addressLine1UrbanOptionPlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { + 'child.birthLocation.privateHome': { streetLevelDetails: { residentialArea: data }, }, }), 'birth.child.addressLine2UrbanOptionPlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { streetLevelDetails: { street: data } }, + 'child.birthLocation.privateHome': { streetLevelDetails: { street: data } }, }), 'birth.child.addressLine3UrbanOptionPlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { streetLevelDetails: { number: data } }, + 'child.birthLocation.privateHome': { streetLevelDetails: { number: data } }, }), 'birth.child.addressLine1RuralOptionPlaceofbirth': (data: string) => ({ - 'child.address.privateHome': { + 'child.birthLocation.privateHome': { streetLevelDetails: { residentialArea: data }, }, }), + // Birth - Child Address + 'birth.child.child-view-group.countryPrimaryChild': (data: string) => ({ + 'child.address': { country: data }, + }), + 'birth.child.child-view-group.statePrimaryChild': (data: string) => ({ + 'child.address': { state: data }, + }), + 'birth.child.child-view-group.districtPrimaryChild': (data: string) => ({ + 'child.address': { administrativeArea: data }, + }), + 'birth.child.child-view-group.cityPrimaryChild': (data: string) => ({ + 'child.address': { streetLevelDetails: { city: data } }, + }), + 'birth.child.child-view-group.addressLine1PrimaryChild': (data: string) => ({ + 'child.address': { streetLevelDetails: { addressLine1: data } }, + }), + 'birth.child.child-view-group.addressLine2PrimaryChild': (data: string) => ({ + 'child.address': { streetLevelDetails: { addressLine2: data } }, + }), + 'birth.child.child-view-group.addressLine3PrimaryChild': (data: string) => ({ + 'child.address': { streetLevelDetails: { addressLine3: data } }, + }), + 'birth.child.child-view-group.postalCodePrimaryChild': (data: string) => ({ + 'child.address': { streetLevelDetails: { zipCode: data } }, + }), + 'birth.child.child-view-group.internationalStatePrimaryChild': ( + data: string + ) => ({ + 'child.address': { streetLevelDetails: { state: data } }, + }), + 'birth.child.child-view-group.internationalCityPrimaryChild': ( + data: string + ) => ({ + 'child.address': { streetLevelDetails: { internationalCity: data } }, + }), + // Birth - Informant Address 'birth.informant.countryPrimaryInformant': (data: string) => ({ 'informant.address': { country: data }, @@ -83,7 +135,7 @@ export const ADDRESS_MAPPINGS: Record = { 'informant.address': { administrativeArea: data }, }), 'birth.informant.cityPrimaryInformant': (data: string) => ({ - 'informant.address': { streetLevelDetails: { town: data } }, + 'informant.address': { streetLevelDetails: { city: data } }, }), 'birth.informant.addressLine1PrimaryInformant': (data: string) => ({ 'informant.address': { streetLevelDetails: { residentialArea: data } }, @@ -162,7 +214,7 @@ export const ADDRESS_MAPPINGS: Record = { 'mother.address': { administrativeArea: data }, }), 'birth.mother.cityPrimaryMother': (data: string) => ({ - 'mother.address': { streetLevelDetails: { town: data } }, + 'mother.address': { streetLevelDetails: { city: data } }, }), 'birth.mother.addressLine1PrimaryMother': (data: string) => ({ 'mother.address': { streetLevelDetails: { residentialArea: data } }, @@ -228,7 +280,7 @@ export const ADDRESS_MAPPINGS: Record = { 'father.address': { administrativeArea: data }, }), 'birth.father.cityPrimaryFather': (data: string) => ({ - 'father.address': { streetLevelDetails: { town: data } }, + 'father.address': { streetLevelDetails: { city: data } }, }), 'birth.father.addressLine1PrimaryFather': (data: string) => ({ 'father.address': { streetLevelDetails: { residentialArea: data } }, @@ -291,7 +343,7 @@ export const ADDRESS_MAPPINGS: Record = { 'deceased.address': { administrativeArea: data }, }), 'death.deceased.cityPrimaryDeceased': (data: string) => ({ - 'deceased.address': { streetLevelDetails: { town: data } }, + 'deceased.address': { streetLevelDetails: { city: data } }, }), 'death.deceased.addressLine1PrimaryDeceased': (data: string) => ({ 'deceased.address': { streetLevelDetails: { residentialArea: data } }, @@ -360,7 +412,7 @@ export const ADDRESS_MAPPINGS: Record = { 'eventDetails.deathLocationOther': { administrativeArea: data }, }), 'death.deathEvent.cityPlaceofdeath': (data: string) => ({ - 'eventDetails.deathLocationOther': { streetLevelDetails: { town: data } }, + 'eventDetails.deathLocationOther': { streetLevelDetails: { city: data } }, }), 'death.deathEvent.addressLine1Placeofdeath': (data: string) => ({ 'eventDetails.deathLocationOther': { @@ -444,7 +496,7 @@ export const ADDRESS_MAPPINGS: Record = { 'informant.address': { administrativeArea: data }, }), 'death.informant.cityPrimaryInformant': (data: string) => ({ - 'informant.address': { streetLevelDetails: { town: data } }, + 'informant.address': { streetLevelDetails: { city: data } }, }), 'death.informant.addressLine1PrimaryInformant': (data: string) => ({ 'informant.address': { streetLevelDetails: { residentialArea: data } }, @@ -529,7 +581,7 @@ export const ADDRESS_MAPPINGS: Record = { 'spouse.address': { administrativeArea: data }, }), 'death.spouse.cityPrimarySpouse': (data: string) => ({ - 'spouse.address': { streetLevelDetails: { town: data } }, + 'spouse.address': { streetLevelDetails: { city: data } }, }), 'death.spouse.addressLine1PrimarySpouse': (data: string) => ({ 'spouse.address': { streetLevelDetails: { residentialArea: data } }, diff --git a/v1-to-v2-data-migration/countryData/addressResolver.ts b/v1-to-v2-data-migration/countryData/addressResolver.ts index 0e3a4fa..9f5bec6 100644 --- a/v1-to-v2-data-migration/countryData/addressResolver.ts +++ b/v1-to-v2-data-migration/countryData/addressResolver.ts @@ -1,7 +1,25 @@ -import { Address, AddressLine, EventRegistration } from '../helpers/types.ts' +import { AddressLine, EventRegistration } from '../helpers/types.ts' -const COUNTRY_CODE = 'FAR' //Replace with actual country code -export const COUNTRY_PHONE_CODE = '+26' //Replace with actual country phone code +export const COUNTRY_CODE = 'SOM' //Replace with actual country code +export const COUNTRY_PHONE_CODE = '+252' //Replace with actual country phone code + +// Set up street level details as they are in your country config +// src/form/street-address-configuration.ts +export type StreetLevelDetails = { + city?: string + number?: string + street?: string + residentialArea?: string + zipCode?: string + internationalCity?: string +} + +export interface Address { + addressType?: 'INTERNATIONAL' | 'DOMESTIC' + country?: string + administrativeArea?: string + streetLevelDetails?: StreetLevelDetails +} export function resolveAddress( data: EventRegistration, @@ -16,16 +34,7 @@ export function resolveAddress( addressType: 'INTERNATIONAL', country: address.country, streetLevelDetails: { - state: address.state, - district2: address.district, - cityOrTown: address.city, - addressLine1: address.line.filter(Boolean)[0], - addressLine2: address.line.filter(Boolean)[1], - addressLine3: address.line.filter(Boolean).slice(2).join(', '), - postcodeOrZip: address.postalCode, - /* For potential custom field - kebele: getCustomField(data, 'birth.child.address.kebele'), - */ + internationalCity: address.city, }, } } @@ -35,7 +44,7 @@ export function resolveAddress( country: address.country, administrativeArea: address.district, streetLevelDetails: { - town: address.city, + city: address.city, number: address.line.filter(Boolean)[0], street: address.line.filter(Boolean)[1], residentialArea: address.line.filter(Boolean)[2], diff --git a/v1-to-v2-data-migration/countryData/countryMappings.ts b/v1-to-v2-data-migration/countryData/countryMappings.ts index 64f4c76..7d770b2 100644 --- a/v1-to-v2-data-migration/countryData/countryMappings.ts +++ b/v1-to-v2-data-migration/countryData/countryMappings.ts @@ -7,29 +7,20 @@ */ export const COUNTRY_FIELD_MAPPINGS = { - 'birth.child-view-group.iD': 'child.nid', - 'birth.child-view-group.countryPrimaryChild': 'child.address', - 'birth.child-view-group.statePrimaryChild': 'child.address', - 'birth.child-view-group.districtPrimaryChild': 'child.address', - 'birth.child-view-group.cityPrimaryChild': 'child.address', - 'birth.child-view-group.addressLine1PrimaryChild': 'child.address', - 'birth.child-view-group.addressLine2PrimaryChild': 'child.address', - 'birth.child-view-group.addressLine3PrimaryChild': 'child.address', - 'birth.child-view-group.postalCodePrimaryChild': 'child.address', - 'birth.child-view-group.internationalCityPrimaryChild': 'child.address', - 'birth.child-view-group.birthAttendantName': 'child.attendantName', - 'birth.child-view-group.birthAttendantId': 'child.attedantAtBirthId', - 'birth.informant-view-group.informantID': 'informant.nid', - 'birth.mother-view-group.iD': 'mother.nid', - 'birth.father-view-group.iD': 'father.nid', - 'birth.documents-view-group.uploadDocOther': 'documents.proofOther', - 'death.deceased-view-group.placeOfBirth': 'deceased.placeOfBirth', - 'death.deceased-view-group.birthRegNo': 'deceased.brn', - 'death.deceased-view-group.deceasedID': 'deceased.nid', - 'death.deceased-view-group.occupation': 'deceased.occupation', - 'death.death-event-details.timeOfDeath': 'eventDetails.timeOfDeath', - 'death.informant-view-group.informantID': 'informant.nid', - 'death.informant-view-group.informantType': 'informant.informantType', - 'death.documents-view-group.uploadDocForInformant': - 'documents.proofOfInformant', + 'birth.child.iD': 'child.nid', + 'birth.child.child-view-group.birthAttendantName': 'child.attendantName', + 'birth.child.child-view-group.birthAttendantId': 'child.attedantAtBirthId', + 'birth.informant.informantID': 'informant.nid', + 'birth.mother.iD': 'mother.nid', + 'birth.father.iD': 'father.nid', + 'birth.documents.uploadDocOther': 'documents.proofOther', + 'death.deceased.deceased-view-group.placeOfBirth': 'deceased.placeOfBirth', + 'death.deceased.deceased-view-group.birthRegNo': 'deceased.brn', + 'death.deceased.deceasedID': 'deceased.nid', + 'death.deceased.deceased-view-group.occupation': 'deceased.occupation', + 'death.deathEvent.death-event-details.timeOfDeath': + 'eventDetails.timeOfDeath', + 'death.informant.informantID': 'informant.nid', + 'death.informant.informantType': 'informant.informantType', + 'death.documents.uploadDocForInformant': 'documents.proofOfInformant', } diff --git a/v1-to-v2-data-migration/countryData/countryResolvers.ts b/v1-to-v2-data-migration/countryData/countryResolvers.ts index 8026cb5..50d121d 100644 --- a/v1-to-v2-data-migration/countryData/countryResolvers.ts +++ b/v1-to-v2-data-migration/countryData/countryResolvers.ts @@ -1,59 +1,68 @@ -import { getCustomField, getIdentifier } from "./resolverUtils.ts"; - -function convertChildAddress(data, address) { - if (!address) { - return null; - } +import { getCustomField, getIdentifier } from '../helpers/resolverUtils.ts' +import { EventRegistration } from '../helpers/types.ts' +import { COUNTRY_CODE, resolveAddress } from './addressResolver.ts' +const convertChildAddress = (data: EventRegistration) => { const country = getCustomField( data, - "birth.child.child-view-group.countryPrimaryChild" - ); - const international = country !== "FAR"; //This doesn't work for other countries - if (international) { - return { - addressType: "INTERNATIONAL", - country, - cityOrTown: getCustomField( - data, - "birth.child-view-group.internationalCityPrimaryChild" - ), - }; - } + 'birth.child.child-view-group.countryPrimaryChild' + ) + return { - addressType: "DOMESTIC", - country, - state: getCustomField(data, "birth.child-view-group.statePrimaryChild"), - district: getCustomField( - data, - "birth.child-view-group.districtPrimaryChild" - ), - cityOrTown: getCustomField(data, "birth.child-view-group.cityPrimaryChild"), - addressLine1: getCustomField( - data, - "birth.child-view-group.addressLine1PrimaryChild" - ), - addressLine2: getCustomField( - data, - "birth.child-view-group.addressLine2PrimaryChild" - ), - addressLine3: getCustomField( - data, - "birth.child-view-group.addressLine3PrimaryChild" - ), - postcodeOrZip: getCustomField( + addressType: country === COUNTRY_CODE ? 'DOMESTIC' : 'INTERNATIONAL', + country: country, + administrativeArea: getCustomField( data, - "birth.child-view-group.postalCodePrimaryChild" + 'birth.child-view-group.districtPrimaryChild' ), - }; + streetLevelDetails: { + town: getCustomField(data, 'birth.child-view-group.cityPrimaryChild'), + number: getCustomField( + data, + 'birth.child-view-group.addressLine1PrimaryChild' + ), + street: getCustomField( + data, + 'birth.child-view-group.addressLine2PrimaryChild' + ), + residentialArea: getCustomField( + data, + 'birth.child-view-group.addressLine3PrimaryChild' + ), + zipCode: getCustomField( + data, + 'birth.child-view-group.postalCodePrimaryChild' + ), + internationalCity: getCustomField( + data, + 'birth.child-view-group.internationalCityPrimaryChild' + ), + }, + } } export const countryResolver = { - "child.nid": (data) => getIdentifier(data.child, "NATIONAL_ID"), - "child.address": (data) => - convertChildAddress(data, data.eventLocation.address), - "child.attendantName": (data) => - getCustomField(data, "birth.child.child-view-group.birthAttendantName"), - "child.attedantAtBirthId": (data) => - getCustomField(data, "birth.child.child-view-group.birthAttendantId"), -}; + 'child.nid': (data: EventRegistration) => + getIdentifier(data.child, 'NATIONAL_ID'), + 'child.address': (data: EventRegistration) => convertChildAddress(data), + 'child.attendantName': (data: EventRegistration) => + getCustomField(data, 'birth.child.child-view-group.birthAttendantName'), + 'child.attedantAtBirthId': (data: EventRegistration) => + getCustomField(data, 'birth.child.child-view-group.birthAttendantId'), + 'child.birthLocation.privateHome': (data: EventRegistration) => + data.eventLocation?.type === 'PRIVATE_HOME' + ? resolveAddress(data, data.eventLocation?.address) + : null, + 'child.birthLocation.other': (data: EventRegistration) => + data.eventLocation?.type === 'OTHER' + ? resolveAddress(data, data.eventLocation?.address) + : null, + 'deceased.placeOfBirth': (data: EventRegistration) => + getCustomField(data, 'death.deceased.deceased-view-group.placeOfBirth'), + 'deceased.occupation': (data: EventRegistration) => + getCustomField(data, 'death.deceased.deceased-view-group.occupation'), + 'eventDetails.timeOfDeath': (data: EventRegistration) => + getCustomField(data, 'death.deathEvent.death-event-details.timeOfDeath'), + 'informant.informantType': (data: EventRegistration) => + data.informant?.relationship, +} diff --git a/v1-to-v2-data-migration/get-field-diff.ipynb b/v1-to-v2-data-migration/get-field-diff.ipynb index 07b6bbe..be62732 100644 --- a/v1-to-v2-data-migration/get-field-diff.ipynb +++ b/v1-to-v2-data-migration/get-field-diff.ipynb @@ -23,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "628ffaa7", "metadata": { "execution": { @@ -33,28 +33,17 @@ "shell.execute_reply": "2025-08-11T16:16:03.160572Z" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[32m\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWNvcmQuZGVjbGFyZS1iaXJ0aCIsInJlY29yZC5kZWNsYXJlLWRlYXRoIiwicmVjb3JkLmRlY2xhcmUtbWFycmlhZ2UiLCJyZWNvcmQuZGVjbGFyYXRpb24tZWRpdCIsInJlY29yZC5kZWNsYXJhdGlvbi1zdWJtaXQtZm9yLXVwZGF0ZXMiLCJyZWNvcmQucmV2aWV3LWR1cGxpY2F0ZXMiLCJyZWNvcmQuZGVjbGFyYXRpb24tYXJjaGl2ZSIsInJlY29yZC5kZWNsYXJhdGlvbi1yZWluc3RhdGUiLCJyZWNvcmQucmVnaXN0ZXIiLCJyZWNvcmQucmVnaXN0cmF0aW9uLWNvcnJlY3QiLCJyZWNvcmQuZGVjbGFyYXRpb24tcHJpbnQtc3VwcG9ydGluZy1kb2N1bWVudHMiLCJyZWNvcmQuZXhwb3J0LXJlY29yZHMiLCJyZWNvcmQudW5hc3NpZ24tb3RoZXJzIiwicmVjb3JkLnJlZ2lzdHJhdGlvbi1wcmludCZpc3N1ZS1jZXJ0aWZpZWQtY29waWVzIiwicmVjb3JkLmNvbmZpcm0tcmVnaXN0cmF0aW9uIiwicmVjb3JkLnJlamVjdC1yZWdpc3RyYXRpb24iLCJwZXJmb3JtYW5jZS5yZWFkIiwicGVyZm9ybWFuY2UucmVhZC1kYXNoYm9hcmRzIiwicGVyZm9ybWFuY2Uudml0YWwtc3RhdGlzdGljcy1leHBvcnQiLCJwcm9maWxlLmVsZWN0cm9uaWMtc2lnbmF0dXJlIiwib3JnYW5pc2F0aW9uLnJlYWQtbG9jYXRpb25zOm15LW9mZmljZSIsInNlYXJjaC5iaXJ0aCIsInNlYXJjaC5kZWF0aCIsInNlYXJjaC5tYXJyaWFnZSIsInVzZXIucmVhZDpteS1vZmZpY2UiLCJzZWFyY2hbZXZlbnQ9djIuYmlydGgsYWNjZXNzPWFsbF0iLCJzZWFyY2hbZXZlbnQ9djIuZGVhdGgsYWNjZXNzPWFsbF0iLCJ3b3JrcXVldWVbaWQ9YXNzaWduZWQtdG8teW91fHJlY2VudHxyZXF1aXJlcy1jb21wbGV0aW9ufHJlcXVpcmVzLXVwZGF0ZXMtb2ZmaWNlfGluLXJldmlldy1hbGx8aW4tZXh0ZXJuYWwtdmFsaWRhdGlvbnxyZWFkeS10by1wcmludHxyZWFkeS10by1pc3N1ZV0iLCJkZW1vIl0sInVzZXJUeXBlIjoidXNlciIsImlhdCI6MTc1NjIxMTc5NiwiZXhwIjoxNzU2ODE2NTk2LCJhdWQiOlsib3BlbmNydnM6YXV0aC11c2VyIiwib3BlbmNydnM6dXNlci1tZ250LXVzZXIiLCJvcGVuY3J2czpoZWFydGgtdXNlciIsIm9wZW5jcnZzOmdhdGV3YXktdXNlciIsIm9wZW5jcnZzOm5vdGlmaWNhdGlvbi11c2VyIiwib3BlbmNydnM6d29ya2Zsb3ctdXNlciIsIm9wZW5jcnZzOnNlYXJjaC11c2VyIiwib3BlbmNydnM6bWV0cmljcy11c2VyIiwib3BlbmNydnM6Y291bnRyeWNvbmZpZy11c2VyIiwib3BlbmNydnM6d2ViaG9va3MtdXNlciIsIm9wZW5jcnZzOmNvbmZpZy11c2VyIiwib3BlbmNydnM6ZG9jdW1lbnRzLXVzZXIiXSwiaXNzIjoib3BlbmNydnM6YXV0aC1zZXJ2aWNlIiwic3ViIjoiNjhhZDgzZjVjNmU5M2QyOTk2M2YzZjBhIn0.yIrOhJp4wPMCZ1Hr062WVKLmfVKg3HTTuB0Qg6fKpfAcK8MiEEj0VSQwppm9jDPQoG6SZFsACmS9DHKGy4rv83evLcuhNO3ir0Y8Xlmc8TuO3kuwUwrxzXYHhL1O9Nxkz7XL9kWFgZ6jvAue1GrhrZmASsxL97nlTqj33hB0pBTudobzUlvxKJg49HMd9txt8vM1xLYJnykHDWK6z9Ak2g3Mxai4j3ib5raN3dL3U30_0aoLHegHHretOjP1ASbhet-ImN7tgwozdlWfz-GEHmbVAgWJ6_T2GxEWm9uqE1Cvw1SJvmBAKabHHYyxxjUccxqxlV3LbrTWZeNsx4jH5w\"\u001b[39m" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import { authenticate } from './helpers/authentication.ts'\n", "\n", - "const token = await authenticate(GATEWAY, 's.faisal', 'test')\n", + "const token = await authenticate('s.faisal', 'test')\n", "token\n" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "7ffd8b2c", "metadata": { "execution": { @@ -78,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "b4367b4e", "metadata": { "execution": { @@ -88,111 +77,7 @@ "shell.execute_reply": "2025-08-11T16:16:03.287888Z" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "[\n", - " \u001b[32m\"birth.child-view-group.firstNamesEng\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.familyNameEng\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.childBirthDate\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.gender\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.iD\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.placeOfBirth\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.birthLocation\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.countryPlaceofbirth\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.statePlaceofbirth\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.districtPlaceofbirth\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.cityPlaceofbirth\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.addressLine1Placeofbirth\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.addressLine2Placeofbirth\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.addressLine3Placeofbirth\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.postalCodePlaceofbirth\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.internationalCityPlaceofbirth\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.countryPrimaryChild\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.statePrimaryChild\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.districtPrimaryChild\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.cityPrimaryChild\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.addressLine1PrimaryChild\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.addressLine2PrimaryChild\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.addressLine3PrimaryChild\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.postalCodePrimaryChild\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.internationalCityPrimaryChild\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.attendantAtBirth\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.birthAttendantName\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.birthAttendantId\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.birthType\"\u001b[39m,\n", - " \u001b[32m\"birth.child-view-group.weightAtBirth\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.informantType\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.otherInformantType\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.firstNamesEng\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.familyNameEng\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.informantBirthDate\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.exactDateOfBirthUnknown\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.ageOfIndividualInYears\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.nationality\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.informantID\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.countryPrimaryInformant\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.statePrimaryInformant\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.districtPrimaryInformant\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.cityPrimaryInformant\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.addressLine1PrimaryInformant\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.addressLine2PrimaryInformant\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.addressLine3PrimaryInformant\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.postalCodePrimaryInformant\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.internationalCityPrimaryInformant\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.registrationPhone\"\u001b[39m,\n", - " \u001b[32m\"birth.informant-view-group.registrationEmail\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.detailsExist\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.reasonNotApplying\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.firstNamesEng\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.familyNameEng\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.motherBirthDate\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.exactDateOfBirthUnknown\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.ageOfIndividualInYears\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.nationality\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.iD\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.countryPrimaryMother\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.statePrimaryMother\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.districtPrimaryMother\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.cityPrimaryMother\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.addressLine1PrimaryMother\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.addressLine2PrimaryMother\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.addressLine3PrimaryMother\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.postalCodePrimaryMother\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.internationalCityPrimaryMother\"\u001b[39m,\n", - " \u001b[32m\"birth.mother-view-group.multipleBirth\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.detailsExist\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.reasonNotApplying\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.firstNamesEng\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.familyNameEng\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.fatherBirthDate\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.exactDateOfBirthUnknown\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.ageOfIndividualInYears\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.nationality\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.iD\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.countryPrimaryFather\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.statePrimaryFather\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.districtPrimaryFather\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.cityPrimaryFather\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.addressLine1PrimaryFather\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.addressLine2PrimaryFather\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.addressLine3PrimaryFather\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.postalCodePrimaryFather\"\u001b[39m,\n", - " \u001b[32m\"birth.father-view-group.internationalCityPrimaryFather\"\u001b[39m,\n", - " \u001b[32m\"birth.documents-view-group.uploadDocForChildDOB\"\u001b[39m,\n", - " \u001b[32m\"birth.documents-view-group.uploadDocForMother\"\u001b[39m,\n", - " \u001b[32m\"birth.documents-view-group.uploadDocForFather\"\u001b[39m,\n", - " \u001b[32m\"birth.documents-view-group.uploadDocForInformant\"\u001b[39m,\n", - " \u001b[32m\"birth.documents-view-group.uploadDocOther\"\u001b[39m\n", - "]" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import { extractFieldType, extractFormFields } from './helpers/utils.ts'\n", "\n", @@ -231,12 +116,19 @@ " .map((f) => f.id)\n", " .filter((x) => x)\n", " ),\n", - "]\n" + "]\n", + "\n", + "Deno.writeFileSync(\n", + " './countryData/v1FormFields.json',\n", + " new TextEncoder().encode(JSON.stringify([birthFormFields, deathFormFields], null, 2)))\n", + "Deno.writeFileSync(\n", + " './countryData/v2EventFields.json',\n", + " new TextEncoder().encode(JSON.stringify([birthEventFields, deathEventFields], null, 2)))\n" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "1bfd59ab", "metadata": { "execution": { @@ -337,7 +229,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "475c7d11", "metadata": { "execution": { @@ -347,157 +239,7 @@ "shell.execute_reply": "2025-08-11T16:16:03.314888Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Unmapped V1 form fields:\n", - "\n", - "Birth:\n", - "None \u001b[32m✔\u001b[0m\n", - "Death:\n", - "None \u001b[32m✔\u001b[0m\n", - "\n", - "V1 form fields mapped to a V2 field that does not exist:\n", - "Birth:\n", - "None \u001b[32m✔\u001b[0m\n", - "Death:\n", - "None \u001b[32m✔\u001b[0m\n", - "\n", - "V1 form fields mapped but V2 field does not have a resolver:\n", - "Birth:\n", - "┌───────┬────────────────────────────────────────────────────────┬───────────────────────────┐\n", - "│ (idx) │ V1 Field │ V2 Field │\n", - "├───────┼────────────────────────────────────────────────────────┼───────────────────────────┤\n", - "│ 0 │ \"birth.child-view-group.iD\" │ \"child.nid\" │\n", - "│ 1 │ \"birth.child-view-group.countryPrimaryChild\" │ \"child.address\" │\n", - "│ 2 │ \"birth.child-view-group.statePrimaryChild\" │ \"child.address\" │\n", - "│ 3 │ \"birth.child-view-group.districtPrimaryChild\" │ \"child.address\" │\n", - "│ 4 │ \"birth.child-view-group.cityPrimaryChild\" │ \"child.address\" │\n", - "│ 5 │ \"birth.child-view-group.addressLine1PrimaryChild\" │ \"child.address\" │\n", - "│ 6 │ \"birth.child-view-group.addressLine2PrimaryChild\" │ \"child.address\" │\n", - "│ 7 │ \"birth.child-view-group.addressLine3PrimaryChild\" │ \"child.address\" │\n", - "│ 8 │ \"birth.child-view-group.postalCodePrimaryChild\" │ \"child.address\" │\n", - "│ 9 │ \"birth.child-view-group.internationalCityPrimaryChild\" │ \"child.address\" │\n", - "│ 10 │ \"birth.child-view-group.birthAttendantName\" │ \"child.attendantName\" │\n", - "│ 11 │ \"birth.child-view-group.birthAttendantId\" │ \"child.attedantAtBirthId\" │\n", - "└───────┴────────────────────────────────────────────────────────┴───────────────────────────┘\n", - "Death:\n", - "┌───────┬────────────────────────────────────────────┬────────────────────────────┐\n", - "│ (idx) │ V1 Field │ V2 Field │\n", - "├───────┼────────────────────────────────────────────┼────────────────────────────┤\n", - "│ 0 │ \"death.deceased-view-group.placeOfBirth\" │ \"deceased.placeOfBirth\" │\n", - "│ 1 │ \"death.deceased-view-group.occupation\" │ \"deceased.occupation\" │\n", - "│ 2 │ \"death.death-event-details.timeOfDeath\" │ \"eventDetails.timeOfDeath\" │\n", - "│ 3 │ \"death.informant-view-group.informantType\" │ \"informant.informantType\" │\n", - "└───────┴────────────────────────────────────────────┴────────────────────────────┘\n", - "\n", - "Additional V2 form fields not mapped:\n", - "Birth:\n", - "┌───────┬───────────────────────────────────────────────────────────────┐\n", - "│ (idx) │ Values │\n", - "├───────┼───────────────────────────────────────────────────────────────┤\n", - "│ 0 │ \"informant.contact\" │\n", - "│ 1 │ \"review.comment\" │\n", - "│ 2 │ \"collector.requesterId\" │\n", - "│ 3 │ \"collector.OTHER.idType\" │\n", - "│ 4 │ \"collector.PASSPORT.details\" │\n", - "│ 5 │ \"collector.nid\" │\n", - "│ 6 │ \"collector.DRIVING_LICENSE.details\" │\n", - "│ 7 │ \"collector.brn\" │\n", - "│ 8 │ \"collector.REFUGEE_NUMBER.details\" │\n", - "│ 9 │ \"collector.ALIEN_NUMBER.details\" │\n", - "│ 10 │ \"collector.OTHER.idTypeOther\" │\n", - "│ 11 │ \"collector.OTHER.idNumberOther\" │\n", - "│ 12 │ \"collector.OTHER.name\" │\n", - "│ 13 │ \"collector.OTHER.relationshipToChild\" │\n", - "│ 14 │ \"collector.OTHER.signedAffidavit\" │\n", - "│ 15 │ \"collector.identity.verify.data.mother\" │\n", - "│ 16 │ \"collector.identity.verify.data.father\" │\n", - "│ 17 │ \"collector.identity.verify.data.other\" │\n", - "│ 18 │ \"collector.collect.payment.data.afterLateRegistrationTarget\" │\n", - "│ 19 │ \"collector.collect.payment.data.inBetweenRegistrationTargets\" │\n", - "│ 20 │ \"collector.collect.payment.data.beforeRegistrationTarget\" │\n", - "│ 21 │ \"requester.type\" │\n", - "│ 22 │ \"requester.idType\" │\n", - "│ 23 │ \"requester.nid\" │\n", - "│ 24 │ \"requester.passport\" │\n", - "│ 25 │ \"requester.brn\" │\n", - "│ 26 │ \"requester.name\" │\n", - "│ 27 │ \"requester.relationship\" │\n", - "│ 28 │ \"reason.option\" │\n", - "│ 29 │ \"reason.other\" │\n", - "│ 30 │ \"requester.identity.verify.data\" │\n", - "│ 31 │ \"documents.supportingDocs\" │\n", - "│ 32 │ \"fees.amount\" │\n", - "│ 33 │ \"child.birthLocation.privateHome\" │\n", - "│ 34 │ \"child.birthLocation.other\" │\n", - "│ 35 │ \"father.addressSameAs\" │\n", - "└───────┴───────────────────────────────────────────────────────────────┘\n", - "Death:\n", - "┌───────┬───────────────────────────────────────────────────────────┐\n", - "│ (idx) │ Values │\n", - "├───────┼───────────────────────────────────────────────────────────┤\n", - "│ 0 │ \"informant.contact\" │\n", - "│ 1 │ \"review.comment\" │\n", - "│ 2 │ \"collector.requesterId\" │\n", - "│ 3 │ \"collector.OTHER.idType\" │\n", - "│ 4 │ \"collector.PASSPORT.details\" │\n", - "│ 5 │ \"collector.nid\" │\n", - "│ 6 │ \"collector.DRIVING_LICENSE.details\" │\n", - "│ 7 │ \"collector.brn\" │\n", - "│ 8 │ \"collector.REFUGEE_NUMBER.details\" │\n", - "│ 9 │ \"collector.ALIEN_NUMBER.details\" │\n", - "│ 10 │ \"collector.OTHER.idTypeOther\" │\n", - "│ 11 │ \"collector.OTHER.idNumberOther\" │\n", - "│ 12 │ \"collector.OTHER.name\" │\n", - "│ 13 │ \"collector.OTHER.relationshipToDeceased\" │\n", - "│ 14 │ \"collector.OTHER.signedAffidavit\" │\n", - "│ 15 │ \"collector.identity.verify.data.spouse\" │\n", - "│ 16 │ \"collector.identity.verify.data.other\" │\n", - "│ 17 │ \"collector.collect.payment.data.afterRegistrationTarget\" │\n", - "│ 18 │ \"collector.collect.payment.data.beforeRegistrationTarget\" │\n", - "│ 19 │ \"requester.type\" │\n", - "│ 20 │ \"requester.idType\" │\n", - "│ 21 │ \"requester.nid\" │\n", - "│ 22 │ \"requester.passport\" │\n", - "│ 23 │ \"requester.brn\" │\n", - "│ 24 │ \"requester.name\" │\n", - "│ 25 │ \"requester.relationship\" │\n", - "│ 26 │ \"reason.option\" │\n", - "│ 27 │ \"reason.other\" │\n", - "│ 28 │ \"requester.identity.verify.data\" │\n", - "│ 29 │ \"documents.supportingDocs\" │\n", - "│ 30 │ \"fees.amount\" │\n", - "│ 31 │ \"informant.addressSameAs\" │\n", - "│ 32 │ \"spouse.detailsNotAvailable\" │\n", - "│ 33 │ \"spouse.reason\" │\n", - "│ 34 │ \"spouse.name\" │\n", - "│ 35 │ \"spouse.dob\" │\n", - "│ 36 │ \"spouse.dobUnknown\" │\n", - "│ 37 │ \"spouse.age\" │\n", - "│ 38 │ \"spouse.nationality\" │\n", - "│ 39 │ \"spouse.idType\" │\n", - "│ 40 │ \"spouse.nid\" │\n", - "│ 41 │ \"spouse.passport\" │\n", - "│ 42 │ \"spouse.brn\" │\n", - "│ 43 │ \"spouse.addressSameAs\" │\n", - "│ 44 │ \"spouse.address\" │\n", - "└───────┴───────────────────────────────────────────────────────────┘\n" - ] - }, - { - "ename": "Error", - "evalue": "Migration may not proceed. Please fix errors", - "output_type": "error", - "traceback": [ - "Stack trace:", - "Error: Migration may not proceed. Please fix errors", - " at :45:9" - ] - } - ], + "outputs": [], "source": [ "const render = (list) => {\n", " if (list.length) {\n", diff --git a/v1-to-v2-data-migration/helpers/authentication.ts b/v1-to-v2-data-migration/helpers/authentication.ts index ba00fc0..6891db1 100644 --- a/v1-to-v2-data-migration/helpers/authentication.ts +++ b/v1-to-v2-data-migration/helpers/authentication.ts @@ -45,6 +45,13 @@ export async function getTokenForSystemClient( } ) const res = await authenticateResponse.json() + if (!authenticateResponse.ok) { + throw new Error( + `Failed to get token for system client: ${ + res.message || authenticateResponse.statusText + }` + ) + } return res.token || res.access_token } diff --git a/v1-to-v2-data-migration/helpers/defaultResolvers.ts b/v1-to-v2-data-migration/helpers/defaultResolvers.ts index e49463b..174a31e 100644 --- a/v1-to-v2-data-migration/helpers/defaultResolvers.ts +++ b/v1-to-v2-data-migration/helpers/defaultResolvers.ts @@ -13,7 +13,7 @@ const informantResolver: ResolverMap = { resolveAddress(data, data.informant?.address?.[0]), // type: FieldType.ADDRESS, // @question, is informant.telecom correct or this? 'informant.phoneNo': (data: EventRegistration) => - data.registration.contactPhoneNumber?.replace(COUNTRY_PHONE_CODE, ''), // @todo https://github.com/opencrvs/opencrvs-core/issues/9601 + data.registration.contactPhoneNumber?.replace(COUNTRY_PHONE_CODE, '0'), // @todo https://github.com/opencrvs/opencrvs-core/issues/9601 'informant.email': (data: EventRegistration) => data.registration.contactEmail, // type: FieldType.EMAIL, 'informant.relation': (data: EventRegistration) => diff --git a/v1-to-v2-data-migration/helpers/transform.ts b/v1-to-v2-data-migration/helpers/transform.ts index 56837db..9f3361e 100644 --- a/v1-to-v2-data-migration/helpers/transform.ts +++ b/v1-to-v2-data-migration/helpers/transform.ts @@ -1,3 +1,4 @@ +// @ts-expect-error - Using Deno-style npm import import { v4 as uuidv4 } from 'npm:uuid' import { DEFAULT_FIELD_MAPPINGS, @@ -19,6 +20,7 @@ import { Action, ActionType, } from './types.ts' +import { COUNTRY_CODE } from '../countryData/addressResolver.ts' const mappings = { ...DEFAULT_FIELD_MAPPINGS, ...COUNTRY_FIELD_MAPPINGS } @@ -41,22 +43,32 @@ function patternMatch( const addressMapping = ADDRESS_MAPPINGS[key](value as string) let addressKey = Object.keys(addressMapping)[0] let addressData = null - + let saveAddressKey = addressKey // TODO - How do I make this conditional configurable for countries? - if (addressKey === 'child.address.privateHome') { - addressKey = declaration[addressKey] + if (addressKey === 'child.birthLocation.privateHome') { + saveAddressKey = declaration[addressKey] ? addressKey - : 'child.address.other' + : 'child.birthLocation.other' } - const transformedWithSameKey = transformedData[addressKey] || {} + const transformedWithSameKey = transformedData[saveAddressKey] || {} addressData = addressMapping[addressKey] const currentAddress = declaration[addressKey] || {} - transformedData[addressKey] = { + + const mergedAddress = { ...currentAddress, ...transformedWithSameKey, ...addressData, + } + + transformedData[saveAddressKey] = { + ...mergedAddress, + addressType: + mergedAddress.addressType || + (mergedAddress.country === COUNTRY_CODE + ? 'DOMESTIC' + : 'INTERNATIONAL'), streetLevelDetails: { ...(currentAddress.streetLevelDetails || {}), ...(transformedWithSameKey.streetLevelDetails || {}), @@ -187,7 +199,7 @@ function legacyHistoryItemToV2ActionType( switch (historyItem.action) { case 'REQUESTED_CORRECTION': return { - status: 'Requested', + status: 'Accepted', type: 'REQUEST_CORRECTION' as ActionType, declaration: transformCorrection( historyItem, @@ -204,6 +216,7 @@ function legacyHistoryItemToV2ActionType( } case 'APPROVED_CORRECTION': return { + status: 'Accepted', type: 'APPROVE_CORRECTION' as ActionType, requestId: historyItem.requestId, declaration: {}, diff --git a/v1-to-v2-data-migration/helpers/types.ts b/v1-to-v2-data-migration/helpers/types.ts index a57a0d3..318d18b 100644 --- a/v1-to-v2-data-migration/helpers/types.ts +++ b/v1-to-v2-data-migration/helpers/types.ts @@ -1,3 +1,5 @@ +import { Address } from '../countryData/addressResolver.ts' + // Form-related types export interface FormField { name: string @@ -57,28 +59,6 @@ export interface AddressLine { postalCode?: string } -export interface StreetLevelDetails { - state?: string - district2?: string - cityOrTown?: string - addressLine1?: string - addressLine2?: string - addressLine3?: string - postcodeOrZip?: string - town?: string - number?: string - street?: string - residentialArea?: string - zipCode?: string -} - -export interface Address { - addressType: 'INTERNATIONAL' | 'DOMESTIC' - country: string - administrativeArea?: string - streetLevelDetails: StreetLevelDetails -} - // Name types export interface Name { firstname?: string diff --git a/v1-to-v2-data-migration/helpers/vars.ts b/v1-to-v2-data-migration/helpers/vars.ts index dda5b1a..f843ea0 100644 --- a/v1-to-v2-data-migration/helpers/vars.ts +++ b/v1-to-v2-data-migration/helpers/vars.ts @@ -1,3 +1,4 @@ +// @ts-nocheck - Using Deno-specific environment variables export const DOMAIN = Deno?.env?.get('OPENCRVS_DOMAIN') || 'farajaland-staging.opencrvs.org' export const EVENT = Deno?.env?.get('OPENCRVS_EVENT') || 'birth' diff --git a/v1-to-v2-data-migration/migrate.ipynb b/v1-to-v2-data-migration/migrate.ipynb index 92e0bf8..1ede063 100644 --- a/v1-to-v2-data-migration/migrate.ipynb +++ b/v1-to-v2-data-migration/migrate.ipynb @@ -35,12 +35,13 @@ " clientSecret = CLIENT_SECRET\n", "} else {\n", " // For dev mode\n", - " const adminToken = await authenticate('j.campbell', 'test')\n", + " const adminToken = await authenticate('h.mohamed', 'test')\n", " const systemRegistration = await registerSystem(adminToken)\n", "\n", " clientId = systemRegistration.data.registerSystem.system.clientId\n", " clientSecret = systemRegistration.data.registerSystem.clientSecret\n", - "}\n" + "}\n", + "\n" ] }, { @@ -247,10 +248,7 @@ " }\n", " }\n", "\n", - " console.time(`Post`)\n", - " const result = await bulkImport(transformed, sysToken)\n", - " console.timeEnd(`Post`)\n", - " return result.result.data.json.id\n", + " await bulkImport(transformed, sysToken)\n", "}\n", "\n", "const pageSize = 10000\n", @@ -269,7 +267,7 @@ " console.error(JSON.stringify(birthRegistrations, null, 2))\n", " throw new Error('No data from searchEvents')\n", " }\n", - " console.log(birthRegistrations.data.searchEvents )\n", + " \n", " const birthIds = birthRegistrations.data.searchEvents.results.map(\n", " (x) => x.id\n", " )\n", From 1a75d496c648e8c45ec8408b7ba63938a638beeb Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Mon, 13 Oct 2025 16:24:27 +0200 Subject: [PATCH 03/20] Fix duplicates --- v1-to-v2-data-migration/helpers/transform.ts | 16 ++++++++++------ v1-to-v2-data-migration/helpers/types.ts | 9 +++++++-- v1-to-v2-data-migration/migrate.ipynb | 3 +-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/v1-to-v2-data-migration/helpers/transform.ts b/v1-to-v2-data-migration/helpers/transform.ts index bc39f10..c7507c9 100644 --- a/v1-to-v2-data-migration/helpers/transform.ts +++ b/v1-to-v2-data-migration/helpers/transform.ts @@ -187,8 +187,8 @@ function legacyHistoryItemToV2ActionType( } case 'IN_PROGRESS': return { - type: 'READ' as ActionType, - declaration: {}, + type: 'NOTIFY' as ActionType, + declaration: declaration, } case 'DECLARATION_UPDATED': //TODO - check if this is correct return { @@ -248,8 +248,10 @@ function legacyHistoryItemToV2ActionType( declaration: {}, content: { duplicates: - record.registration.duplicates?.map((x: any) => x.compositionId) || - [], + record.registration.duplicates?.map((x: any) => ({ + id: x.compositionId, + trackingId: x.trackingId, + })) || [], }, } @@ -259,7 +261,7 @@ function legacyHistoryItemToV2ActionType( const actionMap: Record = { MARKED_AS_DUPLICATE: 'MARK_AS_DUPLICATE', - MARKED_AS_NOT_DUPLICATE: 'MARK_NOT_DUPLICATE', + MARKED_AS_NOT_DUPLICATE: 'MARK_AS_NOT_DUPLICATE', DOWNLOADED: 'READ', UNASSIGNED: 'UNASSIGN', VIEWED: 'READ', @@ -325,7 +327,9 @@ export function transform( } else if (historyItem.action === 'REQUESTED_CORRECTION') { historyItem.id = uuidv4() const req = corrections.pop() - req.requestId = historyItem?.id //TODO needs better error handling + if (req) { + req.requestId = historyItem?.id + } } else if (historyItem.action === 'REJECTED_CORRECTION') { corrections.push(historyItem) } else if (historyItem.action === 'APPROVED_CORRECTION') { diff --git a/v1-to-v2-data-migration/helpers/types.ts b/v1-to-v2-data-migration/helpers/types.ts index 318d18b..99d45c9 100644 --- a/v1-to-v2-data-migration/helpers/types.ts +++ b/v1-to-v2-data-migration/helpers/types.ts @@ -213,13 +213,18 @@ export type ActionType = | 'UNASSIGN' | 'DUPLICATE_DETECTED' | 'MARK_AS_DUPLICATE' - | 'MARK_NOT_DUPLICATE' + | 'MARK_AS_NOT_DUPLICATE' | 'READ' export interface ActionContent { templateId?: string reason?: string - duplicates?: string[] + duplicates?: Duplicate[] +} + +export interface Duplicate { + id: string + trackingId?: string } export interface ActionAnnotation { diff --git a/v1-to-v2-data-migration/migrate.ipynb b/v1-to-v2-data-migration/migrate.ipynb index f9b1f1c..e329c9b 100644 --- a/v1-to-v2-data-migration/migrate.ipynb +++ b/v1-to-v2-data-migration/migrate.ipynb @@ -326,8 +326,7 @@ " }\n", " }\n", "\n", - " const result = await bulkImport(transformed, sysToken)\n", - " return result.result.data.json.id\n", + " await bulkImport(transformed, sysToken)\n", "}\n", "\n", "const pageSize = 10000\n", From 95b3a47102d95bf3f96c5a1200ccd5e95a07abbf Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Mon, 13 Oct 2025 16:24:49 +0200 Subject: [PATCH 04/20] Fix event date strange deceased chaining --- v1-to-v2-data-migration/countryData/countryResolvers.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v1-to-v2-data-migration/countryData/countryResolvers.ts b/v1-to-v2-data-migration/countryData/countryResolvers.ts index 50d121d..d57a5ed 100644 --- a/v1-to-v2-data-migration/countryData/countryResolvers.ts +++ b/v1-to-v2-data-migration/countryData/countryResolvers.ts @@ -65,4 +65,6 @@ export const countryResolver = { getCustomField(data, 'death.deathEvent.death-event-details.timeOfDeath'), 'informant.informantType': (data: EventRegistration) => data.informant?.relationship, + 'eventDetails.date': (data: EventRegistration) => + data.deceased?.deceased?.deathDate, } From 7d52bb98b5a27fde72b8d5e8ff1f4ca4867c9eb3 Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Thu, 16 Oct 2025 16:55:17 +0200 Subject: [PATCH 05/20] Add empty string to default fields --- .../countryData/addressResolver.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/v1-to-v2-data-migration/countryData/addressResolver.ts b/v1-to-v2-data-migration/countryData/addressResolver.ts index 592aa80..f38f24e 100644 --- a/v1-to-v2-data-migration/countryData/addressResolver.ts +++ b/v1-to-v2-data-migration/countryData/addressResolver.ts @@ -38,7 +38,7 @@ export function resolveAddress( addressType: 'INTERNATIONAL', country: address.country, streetLevelDetails: { - internationalCity: address.city, + internationalCity: address.city || '', }, } } @@ -48,11 +48,11 @@ export function resolveAddress( country: address.country, administrativeArea: address.district, streetLevelDetails: { - city: address.city, - number: address.line.filter(Boolean)[0], - street: address.line.filter(Boolean)[1], - residentialArea: address.line.filter(Boolean)[2], - zipCode: address.postalCode, + city: address.city || '', + number: address.line.filter(Boolean)[0] || '', + street: address.line.filter(Boolean)[1] || '', + residentialArea: address.line.filter(Boolean)[2] || '', + zipCode: address.postalCode || '', }, } } From 4ea3ffddeabd176b12c088aa4bf5ead3828a2a36 Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Thu, 16 Oct 2025 16:55:32 +0200 Subject: [PATCH 06/20] Remove age tostring so it stays an int --- v1-to-v2-data-migration/helpers/defaultResolvers.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/v1-to-v2-data-migration/helpers/defaultResolvers.ts b/v1-to-v2-data-migration/helpers/defaultResolvers.ts index 174a31e..5b0b2db 100644 --- a/v1-to-v2-data-migration/helpers/defaultResolvers.ts +++ b/v1-to-v2-data-migration/helpers/defaultResolvers.ts @@ -24,9 +24,8 @@ const informantResolver: ResolverMap = { resolveName(data, data.informant?.name?.[0]), // FieldType.TEXT 'informant.dobUnknown': (data: EventRegistration) => data.informant?.exactDateOfBirthUnknown, // FieldType.CHECKBOX - // @question, is this informant.age or informant.ageOfIndividualInYears? 'informant.age': (data: EventRegistration) => - data.informant?.ageOfIndividualInYears?.toString() /* @todo not a fan of this */, + data.informant?.ageOfIndividualInYears, 'informant.nationality': (data: EventRegistration) => data.informant?.nationality?.[0], // FieldType.COUNTRY 'informant.brn': (data: EventRegistration) => From 63a2da1a0038529ff11d42c4916ce0bdfa7c5229 Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Wed, 29 Oct 2025 10:06:39 +0200 Subject: [PATCH 07/20] OCRVS-10743 Fixed child address mapping --- .../countryData/addressMappings.ts | 249 ++---------------- .../countryData/countryResolvers.ts | 21 +- 2 files changed, 38 insertions(+), 232 deletions(-) diff --git a/v1-to-v2-data-migration/countryData/addressMappings.ts b/v1-to-v2-data-migration/countryData/addressMappings.ts index 18cbc28..927f2ca 100644 --- a/v1-to-v2-data-migration/countryData/addressMappings.ts +++ b/v1-to-v2-data-migration/countryData/addressMappings.ts @@ -34,39 +34,9 @@ export const ADDRESS_MAPPINGS: Record = { streetLevelDetails: { zipCode: data }, }, }), - 'birth.child.internationalStatePlaceofbirth': (data: string) => ({ - 'child.birthLocation.privateHome': { - streetLevelDetails: { state: data }, - }, - }), - 'birth.child.internationalDistrictPlaceofbirth': (data: string) => ({ - 'child.birthLocation.privateHome': { - streetLevelDetails: { district2: data }, - }, - }), 'birth.child.internationalCityPlaceofbirth': (data: string) => ({ 'child.birthLocation.privateHome': { - streetLevelDetails: { cityOrTown: data }, - }, - }), - 'birth.child.internationalAddressLine1Placeofbirth': (data: string) => ({ - 'child.birthLocation.privateHome': { - streetLevelDetails: { addressLine1: data }, - }, - }), - 'birth.child.internationalAddressLine2Placeofbirth': (data: string) => ({ - 'child.birthLocation.privateHome': { - streetLevelDetails: { addressLine2: data }, - }, - }), - 'birth.child.internationalAddressLine3Placeofbirth': (data: string) => ({ - 'child.birthLocation.privateHome': { - streetLevelDetails: { addressLine3: data }, - }, - }), - 'birth.child.internationalPostalCodePlaceofbirth': (data: string) => ({ - 'child.birthLocation.privateHome': { - streetLevelDetails: { postcodeOrZip: data }, + streetLevelDetails: { internationalCity: data }, }, }), @@ -93,7 +63,9 @@ export const ADDRESS_MAPPINGS: Record = { 'child.address': { country: data }, }), 'birth.child.child-view-group.statePrimaryChild': (data: string) => ({ - 'child.address': { state: data }, + 'child.address': { + /* Ignore: Only map leaf level */ + }, }), 'birth.child.child-view-group.districtPrimaryChild': (data: string) => ({ 'child.address': { administrativeArea: data }, @@ -102,22 +74,17 @@ export const ADDRESS_MAPPINGS: Record = { 'child.address': { streetLevelDetails: { city: data } }, }), 'birth.child.child-view-group.addressLine1PrimaryChild': (data: string) => ({ - 'child.address': { streetLevelDetails: { addressLine1: data } }, + 'child.address': { streetLevelDetails: { city: data } }, }), 'birth.child.child-view-group.addressLine2PrimaryChild': (data: string) => ({ - 'child.address': { streetLevelDetails: { addressLine2: data } }, + 'child.address': { streetLevelDetails: { number: data } }, }), 'birth.child.child-view-group.addressLine3PrimaryChild': (data: string) => ({ - 'child.address': { streetLevelDetails: { addressLine3: data } }, + 'child.address': { streetLevelDetails: { residentialArea: data } }, }), 'birth.child.child-view-group.postalCodePrimaryChild': (data: string) => ({ 'child.address': { streetLevelDetails: { zipCode: data } }, }), - 'birth.child.child-view-group.internationalStatePrimaryChild': ( - data: string - ) => ({ - 'child.address': { streetLevelDetails: { state: data } }, - }), 'birth.child.child-view-group.internationalCityPrimaryChild': ( data: string ) => ({ @@ -129,7 +96,9 @@ export const ADDRESS_MAPPINGS: Record = { 'informant.address': { country: data }, }), 'birth.informant.statePrimaryInformant': (data: string) => ({ - 'informant.address': { streetLevelDetails: { state: data } }, + 'informant.address': { + /* Ignore: Only map leaf level */ + }, }), 'birth.informant.districtPrimaryInformant': (data: string) => ({ 'informant.address': { administrativeArea: data }, @@ -149,36 +118,8 @@ export const ADDRESS_MAPPINGS: Record = { 'birth.informant.postalCodePrimaryInformant': (data: string) => ({ 'informant.address': { streetLevelDetails: { zipCode: data } }, }), - 'birth.informant.internationalStatePrimaryInformant': (data: string) => ({ - 'informant.address': { - streetLevelDetails: { state: data }, - }, - }), - 'birth.informant.internationalDistrictPrimaryInformant': (data: string) => ({ - 'informant.address': { streetLevelDetails: { district2: data } }, - }), 'birth.informant.internationalCityPrimaryInformant': (data: string) => ({ - 'informant.address': { streetLevelDetails: { cityOrTown: data } }, - }), - 'birth.informant.internationalAddressLine1PrimaryInformant': ( - data: string - ) => ({ - 'informant.address': { streetLevelDetails: { addressLine1: data } }, - }), - 'birth.informant.internationalAddressLine2PrimaryInformant': ( - data: string - ) => ({ - 'informant.address': { streetLevelDetails: { addressLine2: data } }, - }), - 'birth.informant.internationalAddressLine3PrimaryInformant': ( - data: string - ) => ({ - 'informant.address': { streetLevelDetails: { addressLine3: data } }, - }), - 'birth.informant.internationalPostalCodePrimaryInformant': ( - data: string - ) => ({ - 'informant.address': { streetLevelDetails: { postcodeOrZip: data } }, + 'informant.address': { streetLevelDetails: { internationalCity: data } }, }), // Birth - Informant Address (Urban/Rural Options) @@ -230,28 +171,8 @@ export const ADDRESS_MAPPINGS: Record = { 'birth.mother.postalCodePrimaryMother': (data: string) => ({ 'mother.address': { streetLevelDetails: { zipCode: data } }, }), - 'birth.mother.internationalStatePrimaryMother': (data: string) => ({ - 'mother.address': { - streetLevelDetails: { state: data }, - }, - }), - 'birth.mother.internationalDistrictPrimaryMother': (data: string) => ({ - 'mother.address': { streetLevelDetails: { district2: data } }, - }), 'birth.mother.internationalCityPrimaryMother': (data: string) => ({ - 'mother.address': { streetLevelDetails: { cityOrTown: data } }, - }), - 'birth.mother.internationalAddressLine1PrimaryMother': (data: string) => ({ - 'mother.address': { streetLevelDetails: { addressLine1: data } }, - }), - 'birth.mother.internationalAddressLine2PrimaryMother': (data: string) => ({ - 'mother.address': { streetLevelDetails: { addressLine2: data } }, - }), - 'birth.mother.internationalAddressLine3PrimaryMother': (data: string) => ({ - 'mother.address': { streetLevelDetails: { addressLine3: data } }, - }), - 'birth.mother.internationalPostalCodePrimaryMother': (data: string) => ({ - 'mother.address': { streetLevelDetails: { postcodeOrZip: data } }, + 'mother.address': { streetLevelDetails: { internationalCity: data } }, }), // Birth - Mother Address (Urban/Rural Options) @@ -295,28 +216,8 @@ export const ADDRESS_MAPPINGS: Record = { 'birth.father.postalCodePrimaryFather': (data: string) => ({ 'father.address': { streetLevelDetails: { zipCode: data } }, }), - 'birth.father.internationalStatePrimaryFather': (data: string) => ({ - 'father.address': { - streetLevelDetails: { state: data }, - }, - }), - 'birth.father.internationalDistrictPrimaryFather': (data: string) => ({ - 'father.address': { streetLevelDetails: { district2: data } }, - }), 'birth.father.internationalCityPrimaryFather': (data: string) => ({ - 'father.address': { streetLevelDetails: { cityOrTown: data } }, - }), - 'birth.father.internationalAddressLine1PrimaryFather': (data: string) => ({ - 'father.address': { streetLevelDetails: { addressLine1: data } }, - }), - 'birth.father.internationalAddressLine2PrimaryFather': (data: string) => ({ - 'father.address': { streetLevelDetails: { addressLine2: data } }, - }), - 'birth.father.internationalAddressLine3PrimaryFather': (data: string) => ({ - 'father.address': { streetLevelDetails: { addressLine3: data } }, - }), - 'birth.father.internationalPostalCodePrimaryFather': (data: string) => ({ - 'father.address': { streetLevelDetails: { postcodeOrZip: data } }, + 'father.address': { streetLevelDetails: { internationalCity: data } }, }), // Birth - Father Address (Urban/Rural Options) @@ -360,34 +261,8 @@ export const ADDRESS_MAPPINGS: Record = { 'death.deceased.postalCodePrimaryDeceased': (data: string) => ({ 'deceased.address': { streetLevelDetails: { zipCode: data } }, }), - 'death.deceased.internationalStatePrimaryDeceased': (data: string) => ({ - 'deceased.address': { - streetLevelDetails: { state: data }, - }, - }), - 'death.deceased.internationalDistrictPrimaryDeceased': (data: string) => ({ - 'deceased.address': { streetLevelDetails: { district2: data } }, - }), 'death.deceased.internationalCityPrimaryDeceased': (data: string) => ({ - 'deceased.address': { streetLevelDetails: { cityOrTown: data } }, - }), - 'death.deceased.internationalAddressLine1PrimaryDeceased': ( - data: string - ) => ({ - 'deceased.address': { streetLevelDetails: { addressLine1: data } }, - }), - 'death.deceased.internationalAddressLine2PrimaryDeceased': ( - data: string - ) => ({ - 'deceased.address': { streetLevelDetails: { addressLine2: data } }, - }), - 'death.deceased.internationalAddressLine3PrimaryDeceased': ( - data: string - ) => ({ - 'deceased.address': { streetLevelDetails: { addressLine3: data } }, - }), - 'death.deceased.internationalPostalCodePrimaryDeceased': (data: string) => ({ - 'deceased.address': { streetLevelDetails: { postcodeOrZip: data } }, + 'deceased.address': { streetLevelDetails: { internationalCity: data } }, }), // Death - Deceased Address (Urban/Rural Options) @@ -409,7 +284,9 @@ export const ADDRESS_MAPPINGS: Record = { 'eventDetails.deathLocationOther': { country: data }, }), 'death.deathEvent.statePlaceofdeath': (data: string) => ({ - 'eventDetails.deathLocationOther': { streetLevelDetails: { state: data } }, + 'eventDetails.deathLocationOther': { + /* Ignore: Only map leaf level */ + }, }), 'death.deathEvent.districtPlaceofdeath': (data: string) => ({ 'eventDetails.deathLocationOther': { administrativeArea: data }, @@ -434,39 +311,9 @@ export const ADDRESS_MAPPINGS: Record = { streetLevelDetails: { zipCode: data }, }, }), - 'death.deathEvent.internationalStatePlaceofdeath': (data: string) => ({ - 'eventDetails.deathLocationOther': { - streetLevelDetails: { state: data }, - }, - }), - 'death.deathEvent.internationalDistrictPlaceofdeath': (data: string) => ({ - 'eventDetails.deathLocationOther': { - streetLevelDetails: { district2: data }, - }, - }), 'death.deathEvent.internationalCityPlaceofdeath': (data: string) => ({ 'eventDetails.deathLocationOther': { - streetLevelDetails: { cityOrTown: data }, - }, - }), - 'death.deathEvent.internationalAddressLine1Placeofdeath': (data: string) => ({ - 'eventDetails.deathLocationOther': { - streetLevelDetails: { addressLine1: data }, - }, - }), - 'death.deathEvent.internationalAddressLine2Placeofdeath': (data: string) => ({ - 'eventDetails.deathLocationOther': { - streetLevelDetails: { addressLine2: data }, - }, - }), - 'death.deathEvent.internationalAddressLine3Placeofdeath': (data: string) => ({ - 'eventDetails.deathLocationOther': { - streetLevelDetails: { addressLine3: data }, - }, - }), - 'death.deathEvent.internationalPostalCodePlaceofdeath': (data: string) => ({ - 'eventDetails.deathLocationOther': { - streetLevelDetails: { postcodeOrZip: data }, + streetLevelDetails: { internationalCity: data }, }, }), @@ -493,7 +340,9 @@ export const ADDRESS_MAPPINGS: Record = { 'informant.address': { country: data }, }), 'death.informant.statePrimaryInformant': (data: string) => ({ - 'informant.address': { streetLevelDetails: { state: data } }, + 'informant.address': { + /* Ignore: Only map leaf level */ + }, }), 'death.informant.districtPrimaryInformant': (data: string) => ({ 'informant.address': { administrativeArea: data }, @@ -513,36 +362,8 @@ export const ADDRESS_MAPPINGS: Record = { 'death.informant.postalCodePrimaryInformant': (data: string) => ({ 'informant.address': { streetLevelDetails: { zipCode: data } }, }), - 'death.informant.internationalStatePrimaryInformant': (data: string) => ({ - 'informant.address': { - streetLevelDetails: { state: data }, - }, - }), - 'death.informant.internationalDistrictPrimaryInformant': (data: string) => ({ - 'informant.address': { streetLevelDetails: { district2: data } }, - }), 'death.informant.internationalCityPrimaryInformant': (data: string) => ({ - 'informant.address': { streetLevelDetails: { cityOrTown: data } }, - }), - 'death.informant.internationalAddressLine1PrimaryInformant': ( - data: string - ) => ({ - 'informant.address': { streetLevelDetails: { addressLine1: data } }, - }), - 'death.informant.internationalAddressLine2PrimaryInformant': ( - data: string - ) => ({ - 'informant.address': { streetLevelDetails: { addressLine2: data } }, - }), - 'death.informant.internationalAddressLine3PrimaryInformant': ( - data: string - ) => ({ - 'informant.address': { streetLevelDetails: { addressLine3: data } }, - }), - 'death.informant.internationalPostalCodePrimaryInformant': ( - data: string - ) => ({ - 'informant.address': { streetLevelDetails: { postcodeOrZip: data } }, + 'informant.address': { streetLevelDetails: { internationalCity: data } }, }), // Death - Informant Address (Urban/Rural Options) @@ -572,7 +393,9 @@ export const ADDRESS_MAPPINGS: Record = { 'spouse.address': { country: data }, }), 'death.spouse.statePrimarySpouse': (data: string) => ({ - 'spouse.address': { streetLevelDetails: { state: data } }, + 'spouse.address': { + /* Ignore: Only map leaf level */ + }, }), 'death.spouse.districtPrimarySpouse': (data: string) => ({ 'spouse.address': { administrativeArea: data }, @@ -592,28 +415,8 @@ export const ADDRESS_MAPPINGS: Record = { 'death.spouse.postalCodePrimarySpouse': (data: string) => ({ 'spouse.address': { streetLevelDetails: { zipCode: data } }, }), - 'death.spouse.internationalStatePrimarySpouse': (data: string) => ({ - 'spouse.address': { - streetLevelDetails: { state: data }, - }, - }), - 'death.spouse.internationalDistrictPrimarySpouse': (data: string) => ({ - 'spouse.address': { streetLevelDetails: { district2: data } }, - }), 'death.spouse.internationalCityPrimarySpouse': (data: string) => ({ - 'spouse.address': { streetLevelDetails: { cityOrTown: data } }, - }), - 'death.spouse.internationalAddressLine1PrimarySpouse': (data: string) => ({ - 'spouse.address': { streetLevelDetails: { addressLine1: data } }, - }), - 'death.spouse.internationalAddressLine2PrimarySpouse': (data: string) => ({ - 'spouse.address': { streetLevelDetails: { addressLine2: data } }, - }), - 'death.spouse.internationalAddressLine3PrimarySpouse': (data: string) => ({ - 'spouse.address': { streetLevelDetails: { addressLine3: data } }, - }), - 'death.spouse.internationalPostalCodePrimarySpouse': (data: string) => ({ - 'spouse.address': { streetLevelDetails: { postcodeOrZip: data } }, + 'spouse.address': { streetLevelDetails: { internationalCity: data } }, }), // Death - Spouse Address (Urban/Rural Options) diff --git a/v1-to-v2-data-migration/countryData/countryResolvers.ts b/v1-to-v2-data-migration/countryData/countryResolvers.ts index d57a5ed..e7bad5e 100644 --- a/v1-to-v2-data-migration/countryData/countryResolvers.ts +++ b/v1-to-v2-data-migration/countryData/countryResolvers.ts @@ -1,8 +1,8 @@ import { getCustomField, getIdentifier } from '../helpers/resolverUtils.ts' import { EventRegistration } from '../helpers/types.ts' -import { COUNTRY_CODE, resolveAddress } from './addressResolver.ts' +import { Address, COUNTRY_CODE, resolveAddress } from './addressResolver.ts' -const convertChildAddress = (data: EventRegistration) => { +const convertChildAddress = (data: EventRegistration): Address => { const country = getCustomField( data, 'birth.child.child-view-group.countryPrimaryChild' @@ -13,29 +13,32 @@ const convertChildAddress = (data: EventRegistration) => { country: country, administrativeArea: getCustomField( data, - 'birth.child-view-group.districtPrimaryChild' + 'birth.child.child-view-group.districtPrimaryChild' ), streetLevelDetails: { - town: getCustomField(data, 'birth.child-view-group.cityPrimaryChild'), + city: getCustomField( + data, + 'birth.child.child-view-group.cityPrimaryChild' + ), number: getCustomField( data, - 'birth.child-view-group.addressLine1PrimaryChild' + 'birth.child.child-view-group.addressLine1PrimaryChild' ), street: getCustomField( data, - 'birth.child-view-group.addressLine2PrimaryChild' + 'birth.child.child-view-group.addressLine2PrimaryChild' ), residentialArea: getCustomField( data, - 'birth.child-view-group.addressLine3PrimaryChild' + 'birth.child.child-view-group.addressLine3PrimaryChild' ), zipCode: getCustomField( data, - 'birth.child-view-group.postalCodePrimaryChild' + 'birth.child.child-view-group.postalCodePrimaryChild' ), internationalCity: getCustomField( data, - 'birth.child-view-group.internationalCityPrimaryChild' + 'birth.child.child-view-group.internationalCityPrimaryChild' ), }, } From c3ff7d84e53b6905dea1ac549de9228ccacd0398 Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Wed, 29 Oct 2025 10:37:09 +0200 Subject: [PATCH 08/20] OCRVS-10855 Fix deceased.BRN mapping --- v1-to-v2-data-migration/countryData/countryResolvers.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v1-to-v2-data-migration/countryData/countryResolvers.ts b/v1-to-v2-data-migration/countryData/countryResolvers.ts index e7bad5e..0ed467b 100644 --- a/v1-to-v2-data-migration/countryData/countryResolvers.ts +++ b/v1-to-v2-data-migration/countryData/countryResolvers.ts @@ -70,4 +70,6 @@ export const countryResolver = { data.informant?.relationship, 'eventDetails.date': (data: EventRegistration) => data.deceased?.deceased?.deathDate, + 'deceased.brn': (data: EventRegistration) => + getCustomField(data, 'death.deceased.deceased-view-group.birthRegNo'), } From 747ba041c27a3aeaeb893661e7ac09081c49b10c Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Wed, 29 Oct 2025 11:35:27 +0200 Subject: [PATCH 09/20] OCRVS-10861 Fix manner of death mapping --- .../helpers/defaultResolvers.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/v1-to-v2-data-migration/helpers/defaultResolvers.ts b/v1-to-v2-data-migration/helpers/defaultResolvers.ts index af712c4..34db8d8 100644 --- a/v1-to-v2-data-migration/helpers/defaultResolvers.ts +++ b/v1-to-v2-data-migration/helpers/defaultResolvers.ts @@ -6,6 +6,17 @@ import { import { EventRegistration, ResolverMap } from './types.ts' import { resolveName } from '../countryData/nameResolver.ts' +function mapMannerOfDeath(mannerOfDeath: string | undefined): any { + const mannerMap = { + NATURAL_CAUSES: 'MANNER_NATURAL', + ACCIDENT: 'MANNER_ACCIDENT', + HOMICIDE: 'MANNER_HOMICIDE', + SUICIDE: 'MANNER_SUICIDE', + MANNER_UNDETERMINED: 'MANNER_UNDETERMINED', + } + return mannerMap[mannerOfDeath as keyof typeof mannerMap] +} + const informantResolver: ResolverMap = { 'informant.dob': (data: EventRegistration) => data.informant?.birthDate, // type: 'DATE', /* @todo Addresses need to be properly handled */ @@ -98,7 +109,8 @@ export const deathResolver: ResolverMap = { Boolean(data.causeOfDeathEstablished), 'eventDetails.sourceCauseDeath': (data: EventRegistration) => data.causeOfDeathMethod, - 'eventDetails.mannerOfDeath': (data: EventRegistration) => data.mannerOfDeath, + 'eventDetails.mannerOfDeath': (data: EventRegistration) => + mapMannerOfDeath(data.mannerOfDeath), 'eventDetails.placeOfDeath': (data: EventRegistration) => data.eventLocation?.type, 'eventDetails.deathLocation': (data: EventRegistration) => From af54bf32e5e62d50b3de9e14bf8483c22c33d1c1 Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Wed, 29 Oct 2025 13:26:38 +0200 Subject: [PATCH 10/20] OCRVS-10882 Fix proof of death documents --- v1-to-v2-data-migration/helpers/defaultResolvers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v1-to-v2-data-migration/helpers/defaultResolvers.ts b/v1-to-v2-data-migration/helpers/defaultResolvers.ts index 34db8d8..8a3d53e 100644 --- a/v1-to-v2-data-migration/helpers/defaultResolvers.ts +++ b/v1-to-v2-data-migration/helpers/defaultResolvers.ts @@ -61,7 +61,7 @@ const documentsResolver: ResolverMap = { 'documents.proofOfDeceased': (data: EventRegistration) => getDocuments(data, 'DECEASED_ID_PROOF'), 'documents.proofOfDeath': (data: EventRegistration) => - getDocuments(data, 'INFORMANT_ID_PROOF'), // TODO not this + getDocuments(data, 'DECEASED_DEATH_PROOF'), 'documents.proofOfCauseOfDeath': (data: EventRegistration) => getDocuments(data, 'DECEASED_DEATH_PROOF'), } From a312d299dabcdf6c14abaf4d4a780e8f90a7325e Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Mon, 3 Nov 2025 14:24:06 +0200 Subject: [PATCH 11/20] Move constants into if blocks Migrations fails once this is compiled into a single js file Make the action fail if it throws an exception Fix duplicate var name Ignore reindex for now Update logs to make more sense --- .github/workflows/migrate.yml | 1 + v1-to-v2-data-migration/migrate.ipynb | 87 +++++++++++---------------- 2 files changed, 37 insertions(+), 51 deletions(-) diff --git a/.github/workflows/migrate.yml b/.github/workflows/migrate.yml index 8c1205d..e72f0c5 100644 --- a/.github/workflows/migrate.yml +++ b/.github/workflows/migrate.yml @@ -53,6 +53,7 @@ jobs: OPENCRVS_CLIENT_ID: ${{ github.event.inputs.client_id }} OPENCRVS_CLIENT_SECRET: ${{ github.event.inputs.client_secret }} run: | + set -o pipefail echo "script_output<> $GITHUB_OUTPUT deno run --allow-net --allow-env ./tmp/migrate.ts | tee /dev/stderr >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT diff --git a/v1-to-v2-data-migration/migrate.ipynb b/v1-to-v2-data-migration/migrate.ipynb index 63186cc..e7ac887 100644 --- a/v1-to-v2-data-migration/migrate.ipynb +++ b/v1-to-v2-data-migration/migrate.ipynb @@ -226,43 +226,43 @@ " await bulkImport(transformed, sysToken)\n", "}\n", "\n", - "const pageSize = 10000\n", - "const batchSize = 100\n", - "let itemsRemaining = 0\n", - "let page = 1\n", - "\n", "if (EVENT === 'birth') {\n", + " const pageSize = 10000\n", + " const batchSize = 100\n", + " let itemsRemaining = 0\n", + " let page = 1\n", + " let totalProcessed = 0\n", + "\n", " do {\n", " const birthRegistrations = await fetchAllBirthRegistrations(\n", " sysToken,\n", " page,\n", " pageSize\n", " )\n", + "\n", " if (!birthRegistrations.data.searchEvents) {\n", " console.error(JSON.stringify(birthRegistrations, null, 2))\n", " throw new Error('No data from searchEvents')\n", " }\n", - " \n", - " const birthIds = birthRegistrations.data.searchEvents.results.map(\n", - " (x) => x.id\n", - " )\n", - " console.log('BirthIds found: ', birthIds.length)\n", + "\n", + " const { results, totalItems } = birthRegistrations.data.searchEvents\n", + " const birthIds = results.map((x) => x.id)\n", + " console.log(`Processing next page of ${birthIds.length} records`)\n", "\n", " const batches = batch(birthIds, batchSize)\n", "\n", - " for (const batch of batches) {\n", - " const savedId = await migrateBirth(batch)\n", - " saved.push(savedId)\n", + " for (const b of batches) {\n", + " await migrateBirth(b)\n", + " console.log(` - Processed batch of ${b.length} records`)\n", " }\n", "\n", - " const totalItems = birthRegistrations.data.searchEvents.totalItems\n", - " itemsRemaining = Math.max(0, totalItems - page * pageSize)\n", + " totalProcessed += birthIds.length\n", + " itemsRemaining = Math.max(0, totalItems - totalProcessed)\n", "\n", " console.log(\n", - " `Processed ${\n", - " page * pageSize - 1 + birthIds.length\n", - " } of ${totalItems} birth registrations... With ${itemsRemaining} remaining.`\n", + " `Processed ${totalProcessed} of ${totalItems} birth registrations... with ${itemsRemaining} remaining.`\n", " )\n", + "\n", " page += 1\n", " } while (itemsRemaining > 0)\n", "}\n" @@ -329,12 +329,13 @@ " await bulkImport(transformed, sysToken)\n", "}\n", "\n", - "const pageSize = 10000\n", - "const batchSize = 100\n", - "let itemsRemaining = 0\n", - "let page = 1\n", - "\n", "if (EVENT === 'death') {\n", + " const pageSize = 10000\n", + " const batchSize = 100\n", + " let itemsRemaining = 0\n", + " let page = 1\n", + " let totalProcessed = 0\n", + "\n", " do {\n", " const deathRegistrations = await fetchAllDeathRegistrations(\n", " sysToken,\n", @@ -348,22 +349,20 @@ " const deathIds = deathRegistrations.data.searchEvents.results.map(\n", " (x) => x.id\n", " )\n", - " console.log('DeathIds found: ', deathIds.length)\n", + " console.log(`Processing next page of ${deathIds.length} records`)\n", "\n", " const batches = batch(deathIds, batchSize)\n", "\n", " for (const batch of batches) {\n", - " const savedId = await migrateDeath(batch)\n", - " saved.push(savedId)\n", + " await migrateDeath(batch)\n", + " console.log(` - Processed batch of ${batch.length} records`)\n", " }\n", "\n", - " const totalItems = deathRegistrations.data.searchEvents.totalItems\n", - " itemsRemaining = Math.max(0, totalItems - page * pageSize)\n", + " totalProcessed += deathIds.length\n", + " itemsRemaining = Math.max(0, totalItems - totalProcessed)\n", "\n", " console.log(\n", - " `Processed ${\n", - " page * pageSize - 1 + deathIds.length\n", - " } of ${totalItems} death registrations... With ${itemsRemaining} remaining.`\n", + " `Processed ${totalProcessed} of ${totalItems} death registrations... with ${itemsRemaining} remaining.`\n", " )\n", " page += 1\n", " } while (itemsRemaining > 0)\n", @@ -377,10 +376,12 @@ "metadata": {}, "outputs": [], "source": [ - "import { reindex } from \"./helpers/gqlHandlers.ts\";\n", + "// import { reindex } from \"./helpers/gqlHandlers.ts\";\n", "\n", - "const res = await reindex(sysToken);\n", - "res" + "// const reindexResponse = await reindex(sysToken);\n", + "// reindexResponse\n", + "\n", + "// TODO re-enable when core is updated" ] }, { @@ -405,23 +406,7 @@ }, "outputs": [], "source": [ - "import { DOMAIN } from './helpers/vars.ts'\n", - "import { REGISTER_APP } from './helpers/routes.ts'\n", - "\n", - "if (DOMAIN === 'localhost') {\n", - " console.log('🍞 Declarations succesfully migrated:')\n", - " console.log()\n", - " saved.forEach((s) => {\n", - " console.log(\n", - " `You can view the original record [here](${REGISTER_APP}/${s}/viewRecord?V2_EVENTS=false)`\n", - " )\n", - " console.log(\n", - " `and the new migrated record [here](${REGISTER_APP}/events/view/${s}?V2_EVENTS=true)`\n", - " )\n", - " })\n", - "} else {\n", - " console.log('Declarations succesfully migrated')\n", - "}\n" + "console.log('🍞 Declarations succesfully migrated:')\n" ] } ], From 929791c8a843da3e028fc0e4cfdfb2c3507fc3f8 Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Mon, 3 Nov 2025 15:37:36 +0200 Subject: [PATCH 12/20] Fix deaths --- v1-to-v2-data-migration/migrate.ipynb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/v1-to-v2-data-migration/migrate.ipynb b/v1-to-v2-data-migration/migrate.ipynb index e7ac887..8b5f2ab 100644 --- a/v1-to-v2-data-migration/migrate.ipynb +++ b/v1-to-v2-data-migration/migrate.ipynb @@ -251,9 +251,9 @@ "\n", " const batches = batch(birthIds, batchSize)\n", "\n", - " for (const b of batches) {\n", - " await migrateBirth(b)\n", - " console.log(` - Processed batch of ${b.length} records`)\n", + " for (const batch of batches) {\n", + " await migrateBirth(batch)\n", + " console.log(` - Processed batch of ${batch.length} records`)\n", " }\n", "\n", " totalProcessed += birthIds.length\n", @@ -342,13 +342,14 @@ " page,\n", " pageSize\n", " )\n", + "\n", " if (!deathRegistrations.data.searchEvents) {\n", " console.error(JSON.stringify(deathRegistrations, null, 2))\n", " throw new Error('No data from searchEvents')\n", " }\n", - " const deathIds = deathRegistrations.data.searchEvents.results.map(\n", - " (x) => x.id\n", - " )\n", + "\n", + " const { results, totalItems } = deathRegistrations.data.searchEvents\n", + " const deathIds = results.map((x) => x.id)\n", " console.log(`Processing next page of ${deathIds.length} records`)\n", "\n", " const batches = batch(deathIds, batchSize)\n", From 05ff675257c9b0108d0ff5cb67da4963186ebfa5 Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Wed, 5 Nov 2025 08:54:51 +0200 Subject: [PATCH 13/20] Fix informant relation mapping --- v1-to-v2-data-migration/countryData/countryMappings.ts | 2 +- v1-to-v2-data-migration/countryData/countryResolvers.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/v1-to-v2-data-migration/countryData/countryMappings.ts b/v1-to-v2-data-migration/countryData/countryMappings.ts index 7d770b2..94e8d2b 100644 --- a/v1-to-v2-data-migration/countryData/countryMappings.ts +++ b/v1-to-v2-data-migration/countryData/countryMappings.ts @@ -21,6 +21,6 @@ export const COUNTRY_FIELD_MAPPINGS = { 'death.deathEvent.death-event-details.timeOfDeath': 'eventDetails.timeOfDeath', 'death.informant.informantID': 'informant.nid', - 'death.informant.informantType': 'informant.informantType', + 'death.informant.informantType': 'informant.relation', 'death.documents.uploadDocForInformant': 'documents.proofOfInformant', } diff --git a/v1-to-v2-data-migration/countryData/countryResolvers.ts b/v1-to-v2-data-migration/countryData/countryResolvers.ts index 0ed467b..d12ef3b 100644 --- a/v1-to-v2-data-migration/countryData/countryResolvers.ts +++ b/v1-to-v2-data-migration/countryData/countryResolvers.ts @@ -66,7 +66,7 @@ export const countryResolver = { getCustomField(data, 'death.deceased.deceased-view-group.occupation'), 'eventDetails.timeOfDeath': (data: EventRegistration) => getCustomField(data, 'death.deathEvent.death-event-details.timeOfDeath'), - 'informant.informantType': (data: EventRegistration) => + 'informant.relation': (data: EventRegistration) => data.informant?.relationship, 'eventDetails.date': (data: EventRegistration) => data.deceased?.deceased?.deathDate, From d2b2190d824abd4b63c0d5b6e0f190c825dda7fc Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Wed, 5 Nov 2025 14:29:33 +0200 Subject: [PATCH 14/20] Handle age corrections --- v1-to-v2-data-migration/get-field-diff.ipynb | 8 ++++ .../helpers/defaultMappings.ts | 47 +++++++++++++++++-- v1-to-v2-data-migration/helpers/transform.ts | 6 +++ v1-to-v2-data-migration/migrate.ipynb | 8 ++-- 4 files changed, 59 insertions(+), 10 deletions(-) diff --git a/v1-to-v2-data-migration/get-field-diff.ipynb b/v1-to-v2-data-migration/get-field-diff.ipynb index f574a7a..81d0ae5 100644 --- a/v1-to-v2-data-migration/get-field-diff.ipynb +++ b/v1-to-v2-data-migration/get-field-diff.ipynb @@ -181,6 +181,7 @@ "import {\n", " DEFAULT_FIELD_MAPPINGS,\n", " CUSTOM_FIELD_MAPPINGS,\n", + " AGE_MAPPINGS,\n", "} from './helpers/defaultMappings.ts'\n", "import { ADDRESS_MAPPINGS } from './countryData/addressMappings.ts'\n", "import { NAME_MAPPINGS } from './countryData/nameMappings.ts'\n", @@ -204,9 +205,16 @@ " )\n", ")\n", "\n", + "const ageMap = Object.fromEntries(\n", + " Object.entries(AGE_MAPPINGS).map(\n", + " ([f, value]) => [f, Object.keys(value(''))[0]]\n", + " )\n", + ")\n", + "\n", "const allMappings = {\n", " ...DEFAULT_FIELD_MAPPINGS,\n", " ...CUSTOM_FIELD_MAPPINGS,\n", + " ...ageMap,\n", " ...addressMap,\n", " ...nameMap,\n", " ...COUNTRY_FIELD_MAPPINGS,\n", diff --git a/v1-to-v2-data-migration/helpers/defaultMappings.ts b/v1-to-v2-data-migration/helpers/defaultMappings.ts index 44103d2..1316bde 100644 --- a/v1-to-v2-data-migration/helpers/defaultMappings.ts +++ b/v1-to-v2-data-migration/helpers/defaultMappings.ts @@ -18,7 +18,6 @@ export const DEFAULT_FIELD_MAPPINGS = { 'birth.informant.informantType': 'informant.relation', 'birth.informant.otherInformantType': 'informant.other.relation', 'birth.informant.exactDateOfBirthUnknown': 'informant.dobUnknown', - 'birth.informant.ageOfIndividualInYears': 'informant.age', 'birth.informant.nationality': 'informant.nationality', 'birth.mother.motherNationalId': 'mother.nid', @@ -28,7 +27,6 @@ export const DEFAULT_FIELD_MAPPINGS = { 'birth.mother.reasonNotApplying': 'mother.reason', 'birth.mother.motherBirthDate': 'mother.dob', 'birth.mother.exactDateOfBirthUnknown': 'mother.dobUnknown', - 'birth.mother.ageOfIndividualInYears': 'mother.age', 'birth.mother.nationality': 'mother.nationality', 'birth.mother.maritalStatus': 'mother.maritalStatus', 'birth.mother.educationalAttainment': 'mother.educationalAttainment', @@ -42,7 +40,6 @@ export const DEFAULT_FIELD_MAPPINGS = { 'birth.father.reasonNotApplying': 'father.reason', 'birth.father.fatherBirthDate': 'father.dob', 'birth.father.exactDateOfBirthUnknown': 'father.dobUnknown', - 'birth.father.ageOfIndividualInYears': 'father.age', 'birth.father.nationality': 'father.nationality', 'birth.father.maritalStatus': 'father.maritalStatus', 'birth.father.educationalAttainment': 'father.educationalAttainment', @@ -60,7 +57,6 @@ export const DEFAULT_FIELD_MAPPINGS = { 'death.deceased.gender': 'deceased.gender', 'death.deceased.deceasedBirthDate': 'deceased.dob', 'death.deceased.exactDateOfBirthUnknown': 'deceased.dobUnknown', - 'death.deceased.ageOfIndividualInYears': 'deceased.age', 'death.deceased.nationality': 'deceased.nationality', 'death.deceased.deceasedNationalId': 'deceased.nid', 'death.deceased.deceasedPassport': 'deceased.passport', @@ -80,7 +76,6 @@ export const DEFAULT_FIELD_MAPPINGS = { 'death.informant.otherInformantType': 'informant.other.relation', 'death.informant.informantBirthDate': 'informant.dob', 'death.informant.exactDateOfBirthUnknown': 'informant.dobUnknown', - 'death.informant.ageOfIndividualInYears': 'informant.age', 'death.informant.nationality': 'informant.nationality', 'death.informant.informantNationalId': 'informant.nid', 'death.informant.informantPassport': 'informant.passport', @@ -120,3 +115,45 @@ export const CUSTOM_FIELD_MAPPINGS = { 'death.informant.informant-view-group.informantIdType': 'informant.idType', 'death.spouse.spouse-view-group.spouseIdType': 'spouse.idType', } + +export const AGE_MAPPINGS: Record< + string, + (data: string) => Record +> = { + 'birth.mother.ageOfIndividualInYears': (data: string) => ({ + 'mother.age': { + age: data, + asOfDateRef: 'child.dob', + }, + }), + 'birth.father.ageOfIndividualInYears': (data: string) => ({ + 'father.age': { + age: data, + asOfDateRef: 'child.dob', + }, + }), + 'birth.informant.ageOfIndividualInYears': (data: string) => ({ + 'informant.age': { + age: data, + asOfDateRef: 'child.dob', + }, + }), + 'death.deceased.ageOfIndividualInYears': (data: string) => ({ + 'deceased.age': { + age: data, + asOfDateRef: 'eventDetails.date', + }, + }), + 'death.informant.ageOfIndividualInYears': (data: string) => ({ + 'informant.age': { + age: data, + asOfDateRef: 'eventDetails.date', + }, + }), + 'death.spouse.ageOfIndividualInYears': (data: string) => ({ + 'spouse.age': { + age: data, + asOfDateRef: 'eventDetails.date', + }, + }), +} diff --git a/v1-to-v2-data-migration/helpers/transform.ts b/v1-to-v2-data-migration/helpers/transform.ts index 8812e57..72be554 100644 --- a/v1-to-v2-data-migration/helpers/transform.ts +++ b/v1-to-v2-data-migration/helpers/transform.ts @@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'npm:uuid' import { DEFAULT_FIELD_MAPPINGS, CUSTOM_FIELD_MAPPINGS, + AGE_MAPPINGS, } from './defaultMappings.ts' import { COUNTRY_FIELD_MAPPINGS } from '../countryData/countryMappings.ts' import { NAME_MAPPINGS } from '../countryData/nameMappings.ts' @@ -47,6 +48,11 @@ function patternMatch( const nameKey = Object.keys(nameMapping)[0] const existing = transformedData[nameKey] || {} transformedData[nameKey] = { ...existing, ...nameMapping[nameKey] } + } else if (AGE_MAPPINGS[key]) { + const ageMapping = AGE_MAPPINGS[key](value as string) + const ageKey = Object.keys(ageMapping)[0] + const existing = transformedData[ageKey] || {} + transformedData[ageKey] = { ...existing, ...ageMapping[ageKey] } } else if (ADDRESS_MAPPINGS[key]) { const addressMapping = ADDRESS_MAPPINGS[key](value as string) let addressKey = Object.keys(addressMapping)[0] diff --git a/v1-to-v2-data-migration/migrate.ipynb b/v1-to-v2-data-migration/migrate.ipynb index 8b5f2ab..5a7e8eb 100644 --- a/v1-to-v2-data-migration/migrate.ipynb +++ b/v1-to-v2-data-migration/migrate.ipynb @@ -377,12 +377,10 @@ "metadata": {}, "outputs": [], "source": [ - "// import { reindex } from \"./helpers/gqlHandlers.ts\";\n", + "import { reindex } from \"./helpers/gqlHandlers.ts\";\n", "\n", - "// const reindexResponse = await reindex(sysToken);\n", - "// reindexResponse\n", - "\n", - "// TODO re-enable when core is updated" + "const reindexResponse = await reindex(sysToken);\n", + "reindexResponse\n" ] }, { From ace3c8767c7d45b58438c9e7705ad1bc27f972cf Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Thu, 13 Nov 2025 10:46:43 +0200 Subject: [PATCH 15/20] Add VPN details to Somalia --- .github/workflows/migrate-prod.yml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/migrate-prod.yml b/.github/workflows/migrate-prod.yml index 212efda..5748779 100644 --- a/.github/workflows/migrate-prod.yml +++ b/.github/workflows/migrate-prod.yml @@ -34,7 +34,30 @@ jobs: with: ref: ${{ github.ref }} - # ADD VPN SETUP STEPS HERE + - name: Update apt + run: sudo apt update + + - name: Install openconnect + run: sudo apt install -y openconnect + + - name: Install virtualenv + run: | + sudo apt install virtualenvwrapper + sudo apt install virtualenv + virtualenv vpn-slice + source vpn-slice/bin/activate + pip3 install https://github.com/dlenski/vpn-slice/archive/master.zip + + - name: Connect to VPN + run: | + echo "${{ secrets.VPN_PWD }}" | sudo openconnect --syslog -u ${{ secrets.VPN_USER }} --passwd-on-stdin --protocol=${{ secrets.VPN_PROTOCOL }} ${{ secrets.VPN_HOST }}:${{ secrets.VPN_PORT }} --servercert ${{ secrets.VPN_SERVERCERT }} --background -v --no-dtls -s 'vpn-slice/bin/vpn-slice 10.17.0.0/24' + sleep 3 + ip_addr=$(ip -f inet addr show tun0 |sed -En -e 's/.*inet ([0-9.]+).*/\1/p') + sudo ip route add 10.17.30.0/24 via $ip_addr dev tun0 + + - name: Test VPN connection + run: | + ping -c4 ${{ vars.SSH_HOST || secrets.SSH_HOST }} - name: Install dependencies run: | From be36944c5ee4d7b561c3262d05546698045ed7c2 Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Thu, 13 Nov 2025 14:58:59 +0200 Subject: [PATCH 16/20] Ignore reindex for now --- v1-to-v2-data-migration/migrate.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v1-to-v2-data-migration/migrate.ipynb b/v1-to-v2-data-migration/migrate.ipynb index 44b7f10..66d3343 100644 --- a/v1-to-v2-data-migration/migrate.ipynb +++ b/v1-to-v2-data-migration/migrate.ipynb @@ -379,8 +379,8 @@ "source": [ "// import { reindex } from \"./helpers/gqlHandlers.ts\";\n", "\n", - "const reindexResponse = await reindex(sysToken);\n", - "reindexResponse\n" + "// const reindexResponse = await reindex(sysToken);\n", + "// reindexResponse\n" ] }, { From 46a12cc43cb6bbb99d7161a9361378dc48575580 Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Thu, 13 Nov 2025 15:42:16 +0200 Subject: [PATCH 17/20] Add qa option to prod migration action --- .github/workflows/migrate-prod.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/migrate-prod.yml b/.github/workflows/migrate-prod.yml index 5748779..add91fb 100644 --- a/.github/workflows/migrate-prod.yml +++ b/.github/workflows/migrate-prod.yml @@ -11,6 +11,7 @@ on: options: - staging - production + - qa event: description: 'The event to process (birth or death)' required: true From 21498d6777b1e536801b12aecb9dd61c40cc8613 Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Mon, 1 Dec 2025 13:20:53 +0200 Subject: [PATCH 18/20] Fix merge issues --- v1-to-v2-data-migration/get-field-diff.ipynb | 6 ------ v1-to-v2-data-migration/helpers/defaultResolvers.ts | 11 ----------- 2 files changed, 17 deletions(-) diff --git a/v1-to-v2-data-migration/get-field-diff.ipynb b/v1-to-v2-data-migration/get-field-diff.ipynb index 938486f..7ccf4a6 100644 --- a/v1-to-v2-data-migration/get-field-diff.ipynb +++ b/v1-to-v2-data-migration/get-field-diff.ipynb @@ -217,12 +217,6 @@ " )\n", ")\n", "\n", - "const ageMap = Object.fromEntries(\n", - " Object.entries(AGE_MAPPINGS).map(\n", - " ([f, value]) => [f, Object.keys(value(''))[0]]\n", - " )\n", - ")\n", - "\n", "const allMappings = {\n", " ...DEFAULT_FIELD_MAPPINGS,\n", " ...CUSTOM_FIELD_MAPPINGS,\n", diff --git a/v1-to-v2-data-migration/helpers/defaultResolvers.ts b/v1-to-v2-data-migration/helpers/defaultResolvers.ts index 0de35ee..60beee0 100644 --- a/v1-to-v2-data-migration/helpers/defaultResolvers.ts +++ b/v1-to-v2-data-migration/helpers/defaultResolvers.ts @@ -125,17 +125,6 @@ const documentsResolver: ResolverMap = { getDocuments(data, 'DECEASED_DEATH_CAUSE_PROOF'), } -function mapMannerOfDeath(mannerOfDeath: string | undefined): any { - const mannerMap = { - NATURAL_CAUSES: 'MANNER_NATURAL', - ACCIDENT: 'MANNER_ACCIDENT', - HOMICIDE: 'MANNER_HOMICIDE', - SUICIDE: 'MANNER_SUICIDE', - MANNER_UNDETERMINED: 'MANNER_UNDETERMINED', - } - return mannerMap[mannerOfDeath as keyof typeof mannerMap] -} - export const defaultDeathResolver: ResolverMap = { 'deceased.name': (data: EventRegistration) => resolveName(data, data.deceased?.name?.[0]), From 683755081cd3c2a1f86836a816ace79fc153dffe Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Mon, 1 Dec 2025 14:41:27 +0200 Subject: [PATCH 19/20] Add index result check to migrations --- v1-to-v2-data-migration/helpers/utils.ts | 21 +++++++++++++++ v1-to-v2-data-migration/migrate.ipynb | 34 ++++++++++++++++-------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/v1-to-v2-data-migration/helpers/utils.ts b/v1-to-v2-data-migration/helpers/utils.ts index 87c3db3..c417887 100644 --- a/v1-to-v2-data-migration/helpers/utils.ts +++ b/v1-to-v2-data-migration/helpers/utils.ts @@ -48,3 +48,24 @@ export function batch(items: T[], batchSize: number): T[][] { } return batches } + +type IndexResult = { + result: { + data: { + json: { + errors: any + items: any[] + } + } + } +} + +export const getIndexErrors = ( + indexResult: IndexResult +): string[] | undefined => { + if (indexResult.result?.data?.json?.errors) { + return indexResult.result.data.json.items + ?.filter((x: { index: { error: any } }) => x.index?.error) + .map((x: { index: { error: { reason: any } } }) => x.index.error.reason) + } +} diff --git a/v1-to-v2-data-migration/migrate.ipynb b/v1-to-v2-data-migration/migrate.ipynb index f12dbd0..80598e8 100644 --- a/v1-to-v2-data-migration/migrate.ipynb +++ b/v1-to-v2-data-migration/migrate.ipynb @@ -191,7 +191,7 @@ " fetchAllBirthRegistrations,\n", "} from './helpers/gqlHandlers.ts'\n", "import { transform } from './helpers/transform.ts'\n", - "import { batch } from './helpers/utils.ts'\n", + "import { batch, getIndexErrors } from './helpers/utils.ts'\n", "\n", "const saved = []\n", "\n", @@ -227,7 +227,7 @@ " }\n", " }\n", "\n", - " await bulkImport(transformed, sysToken)\n", + " return await bulkImport(transformed, sysToken)\n", "}\n", "\n", "if (EVENT === 'birth') {\n", @@ -250,13 +250,20 @@ "\n", " const { results, totalItems } = birthRegistrations.data.searchEvents\n", " const birthIds = results.map((x) => x.id)\n", - " console.log(`Processing next page of ${birthIds.length} of ${totalItems} total records`)\n", + " console.log(\n", + " `Processing next page of ${birthIds.length} of ${totalItems} total records`\n", + " )\n", "\n", " const batches = batch(birthIds, batchSize)\n", "\n", " for (const batch of batches) {\n", - " await migrateBirth(batch)\n", + " const indexResult = await migrateBirth(batch)\n", " console.log(` - Processed batch of ${batch.length} records`)\n", + " const errors = getIndexErrors(indexResult)\n", + " if (errors) {\n", + " console.error('Errors during bulk import', errors)\n", + " throw new Error(errors)\n", + " }\n", " }\n", "\n", " totalProcessed += birthIds.length\n", @@ -329,11 +336,9 @@ " }\n", " }\n", "\n", - " const result = await bulkImport(transformed, sysToken)\n", - " return result.result.data.json.id\n", + " return await bulkImport(transformed, sysToken)\n", "}\n", "\n", - "\n", "if (EVENT === 'death') {\n", " const pageSize = 1000\n", " const batchSize = 100\n", @@ -353,13 +358,20 @@ " }\n", " const { results, totalItems } = deathRegistrations.data.searchEvents\n", " const deathIds = results.map((x) => x.id)\n", - " console.log(`Processing next page of ${deathIds.length} of ${totalItems} total records`)\n", + " console.log(\n", + " `Processing next page of ${deathIds.length} of ${totalItems} total records`\n", + " )\n", "\n", " const batches = batch(deathIds, batchSize)\n", "\n", " for (const batch of batches) {\n", - " await migrateDeath(batch)\n", + " const indexResult = await migrateDeath(batch)\n", " console.log(` - Processed batch of ${batch.length} records`)\n", + " const errors = getIndexErrors(indexResult)\n", + " if (errors) {\n", + " console.error('Errors during bulk import', errors)\n", + " throw new Error(errors)\n", + " }\n", " }\n", "\n", " totalProcessed += deathIds.length\n", @@ -382,8 +394,8 @@ "source": [ "import { reindex } from \"./helpers/gqlHandlers.ts\";\n", "\n", - "// const reindexResponse = await reindex(sysToken);\n", - "// reindexResponse\n" + "const reindexResponse = await reindex(sysToken);\n", + "reindexResponse\n" ] }, { From e620f2edee1c58f4be2cd4d5f12a4a5450e9e6aa Mon Sep 17 00:00:00 2001 From: Barry Dwyer Date: Wed, 3 Dec 2025 15:11:11 +0200 Subject: [PATCH 20/20] Add somalia specific tests --- .../tests/SOM/address.test.ts | 350 ++++++++++++++++++ .../tests/SOM/childFields.test.ts | 314 ++++++++++++++++ .../tests/SOM/corrections.test.ts | 307 +++++++++++++++ .../tests/SOM/deceasedFields.test.ts | 171 +++++++++ .../tests/SOM/nameResolver.test.ts | 272 ++++++++++++++ .../tests/SOM/verificationStatus.test.ts | 259 +++++++++++++ 6 files changed, 1673 insertions(+) create mode 100644 v1-to-v2-data-migration/tests/SOM/address.test.ts create mode 100644 v1-to-v2-data-migration/tests/SOM/childFields.test.ts create mode 100644 v1-to-v2-data-migration/tests/SOM/corrections.test.ts create mode 100644 v1-to-v2-data-migration/tests/SOM/deceasedFields.test.ts create mode 100644 v1-to-v2-data-migration/tests/SOM/nameResolver.test.ts create mode 100644 v1-to-v2-data-migration/tests/SOM/verificationStatus.test.ts diff --git a/v1-to-v2-data-migration/tests/SOM/address.test.ts b/v1-to-v2-data-migration/tests/SOM/address.test.ts new file mode 100644 index 0000000..e447d0b --- /dev/null +++ b/v1-to-v2-data-migration/tests/SOM/address.test.ts @@ -0,0 +1,350 @@ +import { transform } from '../../helpers/transform.ts' +import { assertEquals } from 'https://deno.land/std@0.210.0/assert/mod.ts' +import { + buildBirthResolver, + buildBirthEventRegistration, + buildDeathResolver, + buildDeathEventRegistration, +} from '../utils/testHelpers.ts' + +// Construct resolvers as in migrate.ipynb +const birthResolver = buildBirthResolver() +const deathResolver = buildDeathResolver() + +Deno.test('SOM address tests - birth events', async (t) => { + await t.step( + 'should resolve child.birthLocation.privateHome for PRIVATE_HOME', + () => { + const registration = buildBirthEventRegistration({ + eventLocation: { + type: 'PRIVATE_HOME', + address: { + line: ['123', 'Main St', 'City', '', '', 'URBAN'], + district: 'District1', + state: 'State1', + city: 'City1', + country: 'SOM', + postalCode: '12345', + }, + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['child.birthLocation.privateHome'] + ?.administrativeArea, + 'District1' + ) + assertEquals( + declareAction?.declaration['child.birthLocation.privateHome']?.country, + 'SOM' + ) + } + ) + + await t.step('should resolve child.birthLocation.other for OTHER', () => { + const registration = buildBirthEventRegistration({ + eventLocation: { + type: 'OTHER', + address: { + line: ['456', 'Other St', 'Town'], + district: 'District2', + state: 'State2', + city: 'City2', + country: 'SOM', + postalCode: '54321', + }, + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['child.birthLocation.other'] + ?.administrativeArea, + 'District2' + ) + }) + + await t.step('should resolve mother.address', () => { + const registration = buildBirthEventRegistration({ + mother: { + address: [ + { + type: 'PRIMARY_ADDRESS', + line: ['10', 'Mother St', 'City', '', '', 'URBAN'], + country: 'SOM', + district: 'District1', + state: 'State1', + city: 'City1', + postalCode: '11111', + }, + ], + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['mother.address']?.country, 'SOM') + assertEquals( + declareAction?.declaration['mother.address']?.administrativeArea, + 'District1' + ) + }) + + await t.step('should resolve father.address', () => { + const registration = buildBirthEventRegistration({ + father: { + address: [ + { + type: 'PRIMARY_ADDRESS', + line: ['20', 'Father Ave', 'Town'], + country: 'SOM', + district: 'District2', + state: 'State2', + city: 'City2', + postalCode: '22222', + }, + ], + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['father.address']?.country, 'SOM') + assertEquals( + declareAction?.declaration['father.address']?.administrativeArea, + 'District2' + ) + }) + + await t.step( + 'should resolve father.addressSameAs when addresses match', + () => { + const sameAddress = { + type: 'PRIMARY_ADDRESS' as const, + line: ['10', 'Same St', 'City'], + country: 'SOM', + district: 'District1', + state: 'State1', + city: 'City1', + postalCode: '11111', + } + + const registration = buildBirthEventRegistration({ + mother: { address: [sameAddress] }, + father: { address: [sameAddress] }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['father.addressSameAs'], 'YES') + } + ) + + await t.step( + 'should resolve father.addressSameAs when addresses differ', + () => { + const registration = buildBirthEventRegistration({ + mother: { + address: [ + { + type: 'PRIMARY_ADDRESS', + line: ['10', 'Mother St'], + country: 'SOM', + district: 'District1', + }, + ], + }, + father: { + address: [ + { + type: 'PRIMARY_ADDRESS', + line: ['20', 'Father Ave'], + country: 'SOM', + district: 'District2', + }, + ], + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['father.addressSameAs'], 'NO') + } + ) + + await t.step( + 'should resolve informant.address for non-special informant', + () => { + const registration = buildBirthEventRegistration({ + informant: { + relationship: 'GRANDFATHER', + address: [ + { + type: 'PRIMARY_ADDRESS', + line: ['30', 'Informant Rd'], + country: 'SOM', + district: 'District3', + state: 'State3', + city: 'City3', + postalCode: '33333', + }, + ], + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['informant.address']?.administrativeArea, + 'District3' + ) + } + ) +}) + +Deno.test('SOM address tests - death events', async (t) => { + await t.step('should resolve deceased.address', () => { + const data = buildDeathEventRegistration() + const result = transform(data, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['deceased.address']?.country, 'SOM') + assertEquals( + declareAction?.declaration['deceased.address']?.administrativeArea, + 'District1' + ) + }) + + await t.step( + 'should resolve eventDetails.deathLocationOther for OTHER', + () => { + const data = buildDeathEventRegistration({ + eventLocation: { + id: 'other-location', + type: 'OTHER', + address: { + line: ['999 Death St'], + district: 'DistrictX', + state: 'StateX', + country: 'SOM', + }, + }, + }) + const result = transform(data, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['eventDetails.deathLocationOther']?.country, + 'SOM' + ) + } + ) + + await t.step( + 'should resolve informant.addressSameAs when addresses match', + () => { + const sharedAddress = { + type: 'PRIMARY_ADDRESS', + line: ['Same St'], + district: 'District1', + state: 'State1', + country: 'SOM', + } + const data = buildDeathEventRegistration({ + deceased: { + ...buildDeathEventRegistration().deceased!, + address: [sharedAddress], + }, + informant: { + ...buildDeathEventRegistration().informant!, + address: [sharedAddress], + }, + }) + const result = transform(data, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['informant.addressSameAs'], 'YES') + } + ) + + await t.step( + 'should resolve informant.addressSameAs when addresses differ', + () => { + const data = buildDeathEventRegistration() + const result = transform(data, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['informant.addressSameAs'], 'NO') + } + ) + + await t.step( + 'should resolve informant.address for non-special informant', + () => { + const data = buildDeathEventRegistration() + const result = transform(data, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['informant.address']?.country, + 'SOM' + ) + } + ) + + await t.step('should resolve spouse.address', () => { + const data = buildDeathEventRegistration() + const result = transform(data, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['spouse.address']?.country, 'SOM') + }) + + await t.step( + 'should resolve spouse.addressSameAs when addresses match', + () => { + const sharedAddress = { + type: 'PRIMARY_ADDRESS', + line: ['Same St'], + district: 'District1', + state: 'State1', + country: 'SOM', + } + const data = buildDeathEventRegistration({ + deceased: { + ...buildDeathEventRegistration().deceased!, + address: [sharedAddress], + }, + spouse: { + ...buildDeathEventRegistration().spouse!, + address: [sharedAddress], + }, + }) + const result = transform(data, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['spouse.addressSameAs'], 'YES') + } + ) + + await t.step( + 'should resolve spouse.addressSameAs when addresses differ', + () => { + const data = buildDeathEventRegistration() + const result = transform(data, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['spouse.addressSameAs'], 'NO') + } + ) +}) diff --git a/v1-to-v2-data-migration/tests/SOM/childFields.test.ts b/v1-to-v2-data-migration/tests/SOM/childFields.test.ts new file mode 100644 index 0000000..c95ad12 --- /dev/null +++ b/v1-to-v2-data-migration/tests/SOM/childFields.test.ts @@ -0,0 +1,314 @@ +import { transform } from '../../helpers/transform.ts' +import { assertEquals } from 'https://deno.land/std@0.210.0/assert/mod.ts' +import { + buildBirthResolver, + buildBirthEventRegistration, +} from '../utils/testHelpers.ts' + +// Construct resolver as in migrate.ipynb +const birthResolver = buildBirthResolver() + +Deno.test('SOM child fields tests', async (t) => { + await t.step('should resolve child.nid when NATIONAL_ID is present', () => { + const registration = buildBirthEventRegistration({ + child: { + identifier: [ + { + type: 'NATIONAL_ID', + id: 'NID123456', + }, + ], + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['child.nid'], 'NID123456') + }) + + await t.step('should not resolve child.nid when no NATIONAL_ID', () => { + const registration = buildBirthEventRegistration({ + child: { + identifier: [ + { + type: 'BIRTH_REGISTRATION_NUMBER', + id: 'BRN123456', + }, + ], + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['child.nid'], undefined) + }) + + await t.step('should resolve child.attendantName', () => { + const registration = buildBirthEventRegistration({ + questionnaire: [ + { + fieldId: 'birth.child.child-view-group.birthAttendantName', + value: 'Dr. Smith Attendant', + }, + ], + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['child.attendantName'], + 'Dr. Smith Attendant' + ) + }) + + await t.step('should resolve child.attedantAtBirthId', () => { + const registration = buildBirthEventRegistration({ + questionnaire: [ + { + fieldId: 'birth.child.child-view-group.birthAttendantId', + value: 'ATTENDANT12345', + }, + ], + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['child.attedantAtBirthId'], + 'ATTENDANT12345' + ) + }) + + await t.step('should resolve custom child address for domestic', () => { + const registration = buildBirthEventRegistration({ + questionnaire: [ + { + fieldId: 'birth.child.child-view-group.countryPrimaryChild', + value: 'SOM', + }, + { + fieldId: 'birth.child.child-view-group.districtPrimaryChild', + value: 'Banadir', + }, + { + fieldId: 'birth.child.child-view-group.cityPrimaryChild', + value: 'Mogadishu', + }, + { + fieldId: 'birth.child.child-view-group.addressLine1PrimaryChild', + value: '123', + }, + { + fieldId: 'birth.child.child-view-group.addressLine2PrimaryChild', + value: 'Main St', + }, + { + fieldId: 'birth.child.child-view-group.addressLine3PrimaryChild', + value: 'Residential Area', + }, + { + fieldId: 'birth.child.child-view-group.postalCodePrimaryChild', + value: '12345', + }, + ], + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['child.address']?.addressType, + 'DOMESTIC' + ) + assertEquals(declareAction?.declaration['child.address']?.country, 'SOM') + assertEquals( + declareAction?.declaration['child.address']?.administrativeArea, + 'Banadir' + ) + assertEquals( + declareAction?.declaration['child.address']?.streetLevelDetails?.city, + 'Mogadishu' + ) + assertEquals( + declareAction?.declaration['child.address']?.streetLevelDetails?.number, + '123' + ) + assertEquals( + declareAction?.declaration['child.address']?.streetLevelDetails?.street, + 'Main St' + ) + assertEquals( + declareAction?.declaration['child.address']?.streetLevelDetails + ?.residentialArea, + 'Residential Area' + ) + assertEquals( + declareAction?.declaration['child.address']?.streetLevelDetails?.zipCode, + '12345' + ) + }) + + await t.step('should resolve custom child address for international', () => { + const registration = buildBirthEventRegistration({ + questionnaire: [ + { + fieldId: 'birth.child.child-view-group.countryPrimaryChild', + value: 'KEN', + }, + { + fieldId: 'birth.child.child-view-group.districtPrimaryChild', + value: 'Nairobi', + }, + { + fieldId: 'birth.child.child-view-group.internationalCityPrimaryChild', + value: 'Nairobi City', + }, + ], + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['child.address']?.addressType, + 'INTERNATIONAL' + ) + assertEquals(declareAction?.declaration['child.address']?.country, 'KEN') + assertEquals( + declareAction?.declaration['child.address']?.administrativeArea, + 'Nairobi' + ) + assertEquals( + declareAction?.declaration['child.address']?.streetLevelDetails + ?.internationalCity, + 'Nairobi City' + ) + }) + + await t.step( + 'should resolve child.birthLocation.privateHome for PRIVATE_HOME', + () => { + const registration = buildBirthEventRegistration({ + eventLocation: { + type: 'PRIVATE_HOME', + address: { + line: ['123', 'Main St', 'Residential Area', '', '', 'URBAN'], + district: 'Banadir', + state: 'Banadir', + city: 'Mogadishu', + country: 'SOM', + postalCode: '12345', + }, + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['child.birthLocation.privateHome'] + ?.addressType, + 'DOMESTIC' + ) + assertEquals( + declareAction?.declaration['child.birthLocation.privateHome']?.country, + 'SOM' + ) + assertEquals( + declareAction?.declaration['child.birthLocation.privateHome'] + ?.administrativeArea, + 'Banadir' + ) + assertEquals( + declareAction?.declaration['child.birthLocation.privateHome'] + ?.streetLevelDetails?.city, + 'Mogadishu' + ) + } + ) + + await t.step( + 'should not resolve child.birthLocation.privateHome for non-PRIVATE_HOME', + () => { + const registration = buildBirthEventRegistration({ + eventLocation: { + type: 'HEALTH_FACILITY', + address: { + line: ['456 Hospital Rd'], + district: 'Banadir', + country: 'SOM', + }, + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['child.birthLocation.privateHome'], + undefined + ) + } + ) + + await t.step('should resolve child.birthLocation.other for OTHER', () => { + const registration = buildBirthEventRegistration({ + eventLocation: { + type: 'OTHER', + address: { + line: ['789', 'Other St', 'Area'], + district: 'Lower Shabelle', + state: 'Lower Shabelle', + city: 'Afgooye', + country: 'SOM', + postalCode: '54321', + }, + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['child.birthLocation.other']?.addressType, + 'DOMESTIC' + ) + assertEquals( + declareAction?.declaration['child.birthLocation.other']?.country, + 'SOM' + ) + assertEquals( + declareAction?.declaration['child.birthLocation.other'] + ?.administrativeArea, + 'Lower Shabelle' + ) + }) + + await t.step( + 'should not resolve child.birthLocation.other for non-OTHER', + () => { + const registration = buildBirthEventRegistration({ + eventLocation: { + type: 'HEALTH_FACILITY', + address: { + line: ['456 Hospital Rd'], + district: 'Banadir', + country: 'SOM', + }, + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['child.birthLocation.other'], + undefined + ) + } + ) +}) diff --git a/v1-to-v2-data-migration/tests/SOM/corrections.test.ts b/v1-to-v2-data-migration/tests/SOM/corrections.test.ts new file mode 100644 index 0000000..96386ea --- /dev/null +++ b/v1-to-v2-data-migration/tests/SOM/corrections.test.ts @@ -0,0 +1,307 @@ +import { transform } from '../../helpers/transform.ts' +import { + buildBirthResolver, + buildDeathResolver, + buildBirthEventRegistration, + buildDeathEventRegistration, +} from '../utils/testHelpers.ts' +import { assertEquals } from 'https://deno.land/std@0.210.0/assert/mod.ts' + +Deno.test('SOM Corrections - Address Fields', async (t) => { + const birthResolver = buildBirthResolver() + const deathResolver = buildDeathResolver() + + await t.step('should transform address field corrections in birth', () => { + const registration = buildBirthEventRegistration({ + history: [ + { + date: '2024-01-01T10:00:00Z', + regStatus: 'DECLARED', + user: { id: 'user1', role: { id: 'FIELD_AGENT' } }, + office: { id: 'office1' }, + }, + { + date: '2024-01-02T12:00:00Z', + action: 'REQUESTED_CORRECTION', + user: { id: 'user2', role: { id: 'REGISTRATION_AGENT' } }, + office: { id: 'office1' }, + input: [ + { + valueCode: 'mother', + valueId: 'internationalCityPrimaryMother', + value: 'OldInternationalCity', + }, + { + valueCode: 'mother', + valueId: 'cityPrimaryMother', + value: 'OldCity', + }, + ], + output: [ + { + valueCode: 'mother', + valueId: 'internationalCityPrimaryMother', + value: 'NewInternationalCity', + }, + { + valueCode: 'mother', + valueId: 'cityPrimaryMother', + value: 'NewCity', + }, + { + valueCode: 'mother', + valueId: 'addressLine1PrimaryMother', + value: 'ResidentialArea', + }, + ], + }, + ], + }) + + const result = transform(registration, birthResolver, 'birth') + const correctionAction = result.actions.find( + (a) => a.type === 'REQUEST_CORRECTION' + ) + + assertEquals( + correctionAction?.declaration?.['mother.address']?.streetLevelDetails + ?.internationalCity, + 'NewInternationalCity' + ) + assertEquals( + correctionAction?.declaration?.['mother.address']?.streetLevelDetails + ?.city, + 'NewCity' + ) + assertEquals( + correctionAction?.declaration?.['mother.address']?.streetLevelDetails + ?.residentialArea, + 'ResidentialArea' + ) + assertEquals( + correctionAction?.annotation?.['mother.address']?.streetLevelDetails + ?.internationalCity, + 'OldInternationalCity' + ) + assertEquals( + correctionAction?.annotation?.['mother.address']?.streetLevelDetails + ?.city, + 'OldCity' + ) + }) + + await t.step('should transform death location address corrections', () => { + const registration = buildDeathEventRegistration({ + history: [ + { + date: '2024-01-01T10:00:00Z', + regStatus: 'DECLARED', + user: { id: 'user1', role: { id: 'FIELD_AGENT' } }, + office: { id: 'office1' }, + }, + { + date: '2024-01-02T12:00:00Z', + action: 'REQUESTED_CORRECTION', + user: { id: 'user2', role: { id: 'REGISTRATION_AGENT' } }, + office: { id: 'office1' }, + input: [ + { + valueCode: 'deathEvent', + valueId: 'internationalCityPlaceofdeath', + value: 'OldInternationalCity', + }, + { + valueCode: 'deathEvent', + valueId: 'cityPlaceofdeath', + value: 'OldCity', + }, + ], + output: [ + { + valueCode: 'deathEvent', + valueId: 'internationalCityPlaceofdeath', + value: 'NewInternationalCity', + }, + { + valueCode: 'deathEvent', + valueId: 'cityPlaceofdeath', + value: 'NewCity', + }, + ], + }, + ], + }) + + const result = transform(registration, deathResolver, 'death') + const correctionAction = result.actions.find( + (a) => a.type === 'REQUEST_CORRECTION' + ) + + assertEquals( + correctionAction?.declaration?.['eventDetails.deathLocationOther'] + ?.streetLevelDetails?.internationalCity, + 'NewInternationalCity' + ) + assertEquals( + correctionAction?.declaration?.['eventDetails.deathLocationOther'] + ?.streetLevelDetails?.city, + 'NewCity' + ) + assertEquals( + correctionAction?.annotation?.['eventDetails.deathLocationOther'] + ?.streetLevelDetails?.internationalCity, + 'OldInternationalCity' + ) + assertEquals( + correctionAction?.annotation?.['eventDetails.deathLocationOther'] + ?.streetLevelDetails?.city, + 'OldCity' + ) + }) + + await t.step( + 'should transform informant address corrections in death', + () => { + const registration = buildDeathEventRegistration({ + history: [ + { + date: '2024-01-01T10:00:00Z', + regStatus: 'DECLARED', + user: { id: 'user1', role: { id: 'FIELD_AGENT' } }, + office: { id: 'office1' }, + }, + { + date: '2024-01-02T12:00:00Z', + action: 'REQUESTED_CORRECTION', + user: { id: 'user2', role: { id: 'REGISTRATION_AGENT' } }, + office: { id: 'office1' }, + input: [ + { + valueCode: 'informant', + valueId: 'internationalCityPrimaryInformant', + value: 'OldInternationalCity', + }, + { + valueCode: 'informant', + valueId: 'cityPrimaryInformant', + value: 'OldCity', + }, + { + valueCode: 'informant', + valueId: 'postalCodePrimaryInformant', + value: '12345', + }, + ], + output: [ + { + valueCode: 'informant', + valueId: 'internationalCityPrimaryInformant', + value: 'NewInternationalCity', + }, + { + valueCode: 'informant', + valueId: 'cityPrimaryInformant', + value: 'NewCity', + }, + { + valueCode: 'informant', + valueId: 'postalCodePrimaryInformant', + value: '54321', + }, + ], + }, + ], + }) + + const result = transform(registration, deathResolver, 'death') + const correctionAction = result.actions.find( + (a) => a.type === 'REQUEST_CORRECTION' + ) + + // Check individual fields to avoid default field interference + assertEquals( + correctionAction?.declaration?.['informant.address']?.streetLevelDetails + ?.internationalCity, + 'NewInternationalCity' + ) + assertEquals( + correctionAction?.declaration?.['informant.address']?.streetLevelDetails + ?.city, + 'NewCity' + ) + assertEquals( + correctionAction?.declaration?.['informant.address']?.streetLevelDetails + ?.zipCode, + '54321' + ) + assertEquals( + correctionAction?.annotation?.['informant.address']?.streetLevelDetails + ?.internationalCity, + 'OldInternationalCity' + ) + assertEquals( + correctionAction?.annotation?.['informant.address']?.streetLevelDetails + ?.city, + 'OldCity' + ) + assertEquals( + correctionAction?.annotation?.['informant.address']?.streetLevelDetails + ?.zipCode, + '12345' + ) + } + ) + + await t.step( + 'should preserve addressType when correcting address fields', + () => { + const registration = buildBirthEventRegistration({ + history: [ + { + date: '2024-01-01T10:00:00Z', + regStatus: 'DECLARED', + user: { id: 'user1', role: { id: 'FIELD_AGENT' } }, + office: { id: 'office1' }, + }, + { + date: '2024-01-02T12:00:00Z', + action: 'REQUESTED_CORRECTION', + user: { id: 'user2', role: { id: 'REGISTRATION_AGENT' } }, + office: { id: 'office1' }, + input: [ + { + valueCode: 'father', + valueId: 'internationalCityPrimaryFather', + value: 'OldInternationalCity', + }, + ], + output: [ + { + valueCode: 'father', + valueId: 'internationalCityPrimaryFather', + value: 'NewInternationalCity', + }, + ], + }, + ], + }) + + const result = transform(registration, birthResolver, 'birth') + const correctionAction = result.actions.find( + (a) => a.type === 'REQUEST_CORRECTION' + ) + + // Should have addressType set based on country/international fields + assertEquals( + correctionAction?.declaration?.['father.address']?.addressType !== + undefined, + true + ) + assertEquals( + correctionAction?.declaration?.['father.address']?.streetLevelDetails + ?.internationalCity, + 'NewInternationalCity' + ) + } + ) +}) diff --git a/v1-to-v2-data-migration/tests/SOM/deceasedFields.test.ts b/v1-to-v2-data-migration/tests/SOM/deceasedFields.test.ts new file mode 100644 index 0000000..3243ff7 --- /dev/null +++ b/v1-to-v2-data-migration/tests/SOM/deceasedFields.test.ts @@ -0,0 +1,171 @@ +import { transform } from '../../helpers/transform.ts' +import { assertEquals } from 'https://deno.land/std@0.210.0/assert/mod.ts' +import { + buildDeathResolver, + buildDeathEventRegistration, +} from '../utils/testHelpers.ts' + +// Construct resolver as in migrate.ipynb +const deathResolver = buildDeathResolver() + +Deno.test('SOM deceased fields tests', async (t) => { + await t.step( + 'should resolve deceased.placeOfBirth from questionnaire', + () => { + const registration = buildDeathEventRegistration({ + questionnaire: [ + { + fieldId: 'death.deceased.deceased-view-group.placeOfBirth', + value: 'Mogadishu, Somalia', + }, + ], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['deceased.placeOfBirth'], + 'Mogadishu, Somalia' + ) + } + ) + + await t.step('should resolve deceased.occupation from questionnaire', () => { + const registration = buildDeathEventRegistration({ + questionnaire: [ + { + fieldId: 'death.deceased.deceased-view-group.occupation', + value: 'Teacher', + }, + ], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['deceased.occupation'], 'Teacher') + }) + + await t.step('should resolve deceased.brn from questionnaire', () => { + const registration = buildDeathEventRegistration({ + questionnaire: [ + { + fieldId: 'death.deceased.deceased-view-group.birthRegNo', + value: 'B2020123456', + }, + ], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['deceased.brn'], 'B2020123456') + }) + + await t.step( + 'should resolve eventDetails.timeOfDeath from questionnaire', + () => { + const registration = buildDeathEventRegistration({ + questionnaire: [ + { + fieldId: 'death.deathEvent.death-event-details.timeOfDeath', + value: '14:30', + }, + ], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['eventDetails.timeOfDeath'], + '14:30' + ) + } + ) + + await t.step('should resolve informant.relation from relationship', () => { + const registration = buildDeathEventRegistration({ + informant: { + relationship: 'SON', + }, + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['informant.relation'], 'SON') + }) + + await t.step( + 'should resolve eventDetails.date from deceased.deathDate', + () => { + const registration = buildDeathEventRegistration({ + deceased: { + deceased: { + deathDate: '2023-12-15', + }, + }, + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['eventDetails.date'], + '2023-12-15' + ) + } + ) + + await t.step('should handle missing deceased.placeOfBirth gracefully', () => { + const registration = buildDeathEventRegistration({ + questionnaire: [], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['deceased.placeOfBirth'], undefined) + }) + + await t.step('should handle missing deceased.occupation gracefully', () => { + const registration = buildDeathEventRegistration({ + questionnaire: [], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['deceased.occupation'], undefined) + }) + + await t.step('should handle missing deceased.brn gracefully', () => { + const registration = buildDeathEventRegistration({ + questionnaire: [], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['deceased.brn'], undefined) + }) + + await t.step( + 'should handle missing eventDetails.timeOfDeath gracefully', + () => { + const registration = buildDeathEventRegistration({ + questionnaire: [], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['eventDetails.timeOfDeath'], + undefined + ) + } + ) +}) diff --git a/v1-to-v2-data-migration/tests/SOM/nameResolver.test.ts b/v1-to-v2-data-migration/tests/SOM/nameResolver.test.ts new file mode 100644 index 0000000..80c9e0c --- /dev/null +++ b/v1-to-v2-data-migration/tests/SOM/nameResolver.test.ts @@ -0,0 +1,272 @@ +import { transform } from '../../helpers/transform.ts' +import { assertEquals } from 'https://deno.land/std@0.210.0/assert/mod.ts' +import { + buildBirthResolver, + buildBirthEventRegistration, + buildDeathResolver, + buildDeathEventRegistration, +} from '../utils/testHelpers.ts' + +// Construct resolvers as in migrate.ipynb +const birthResolver = buildBirthResolver() +const deathResolver = buildDeathResolver() + +Deno.test('SOM name resolver tests - birth events', async (t) => { + await t.step('should resolve child.name with all fields', () => { + const registration = buildBirthEventRegistration({ + child: { + name: [ + { + firstNames: 'Ahmed', + middleName: 'Mohamed', + familyName: 'Hassan', + }, + ], + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['child.name'], { + firstname: 'Ahmed', + middleName: 'Mohamed', + surname: 'Hassan', + }) + }) + + await t.step('should resolve child.name without middleName', () => { + const registration = buildBirthEventRegistration({ + child: { + name: [ + { + firstNames: 'Fatima', + familyName: 'Ali', + }, + ], + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['child.name'], { + firstname: 'Fatima', + middleName: undefined, + surname: 'Ali', + }) + }) + + await t.step('should resolve mother.name with all fields', () => { + const registration = buildBirthEventRegistration({ + mother: { + name: [ + { + firstNames: 'Halima', + middleName: 'Abdi', + familyName: 'Omar', + }, + ], + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['mother.name'], { + firstname: 'Halima', + middleName: 'Abdi', + surname: 'Omar', + }) + }) + + await t.step('should resolve father.name with all fields', () => { + const registration = buildBirthEventRegistration({ + father: { + name: [ + { + firstNames: 'Ibrahim', + middleName: 'Yusuf', + familyName: 'Ahmed', + }, + ], + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['father.name'], { + firstname: 'Ibrahim', + middleName: 'Yusuf', + surname: 'Ahmed', + }) + }) + + await t.step( + 'should resolve informant.name for non-special informant', + () => { + const registration = buildBirthEventRegistration({ + informant: { + relationship: 'GRANDFATHER', + name: [ + { + firstNames: 'Osman', + middleName: 'Ali', + familyName: 'Hassan', + }, + ], + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['informant.name'], { + firstname: 'Osman', + middleName: 'Ali', + surname: 'Hassan', + }) + } + ) + + await t.step('should return null when name array is undefined', () => { + const registration = buildBirthEventRegistration({ + child: { + name: undefined, + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['child.name'], undefined) + }) + + await t.step('should handle empty name array', () => { + const registration = buildBirthEventRegistration({ + child: { + name: [], + }, + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['child.name'], undefined) + }) +}) + +Deno.test('SOM name resolver tests - death events', async (t) => { + await t.step('should resolve deceased.name with all fields', () => { + const registration = buildDeathEventRegistration({ + deceased: { + name: [ + { + firstNames: 'Abdi', + middleName: 'Mohamed', + familyName: 'Aden', + }, + ], + }, + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['deceased.name'], { + firstname: 'Abdi', + middleName: 'Mohamed', + surname: 'Aden', + }) + }) + + await t.step('should resolve informant.name for non-SPOUSE informant', () => { + const registration = buildDeathEventRegistration({ + informant: { + relationship: 'SON', + name: [ + { + firstNames: 'Hassan', + middleName: 'Abdi', + familyName: 'Aden', + }, + ], + }, + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['informant.name'], { + firstname: 'Hassan', + middleName: 'Abdi', + surname: 'Aden', + }) + }) + + await t.step('should not resolve informant.name for SPOUSE', () => { + const registration = buildDeathEventRegistration({ + informant: { + relationship: 'SPOUSE', + name: [ + { + firstNames: 'Amina', + familyName: 'Omar', + }, + ], + }, + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + // SPOUSE is a special informant, so informant.name should not be set + assertEquals(declareAction?.declaration['informant.name'], undefined) + }) + + await t.step('should resolve spouse.name', () => { + const registration = buildDeathEventRegistration({ + spouse: { + name: [ + { + firstNames: 'Khadija', + middleName: 'Ali', + familyName: 'Mohamed', + }, + ], + }, + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['spouse.name'], { + firstname: 'Khadija', + middleName: 'Ali', + surname: 'Mohamed', + }) + }) + + await t.step('should handle missing middleName in deceased', () => { + const registration = buildDeathEventRegistration({ + deceased: { + name: [ + { + firstNames: 'Yusuf', + familyName: 'Ibrahim', + }, + ], + }, + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['deceased.name'], { + firstname: 'Yusuf', + middleName: undefined, + surname: 'Ibrahim', + }) + }) +}) diff --git a/v1-to-v2-data-migration/tests/SOM/verificationStatus.test.ts b/v1-to-v2-data-migration/tests/SOM/verificationStatus.test.ts new file mode 100644 index 0000000..7922ba6 --- /dev/null +++ b/v1-to-v2-data-migration/tests/SOM/verificationStatus.test.ts @@ -0,0 +1,259 @@ +import { transform } from '../../helpers/transform.ts' +import { assertEquals } from 'https://deno.land/std@0.210.0/assert/mod.ts' +import { + buildBirthResolver, + buildBirthEventRegistration, + buildDeathResolver, + buildDeathEventRegistration, +} from '../utils/testHelpers.ts' + +// Construct resolvers as in migrate.ipynb +const birthResolver = buildBirthResolver() +const deathResolver = buildDeathResolver() + +Deno.test('SOM verification status tests - birth events', async (t) => { + await t.step('should map "authenticated" status for mother', () => { + const registration = buildBirthEventRegistration({ + questionnaire: [ + { + fieldId: 'birth.mother.mother-view-group.verified', + value: 'authenticated', + }, + ], + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['mother.verified'], 'authenticated') + }) + + await t.step('should map "verified" status for father', () => { + const registration = buildBirthEventRegistration({ + questionnaire: [ + { + fieldId: 'birth.father.father-view-group.verified', + value: 'verified', + }, + ], + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['father.verified'], 'verified') + }) + + await t.step('should map "failed" status for informant', () => { + const registration = buildBirthEventRegistration({ + questionnaire: [ + { + fieldId: 'birth.informant.informant-view-group.verified', + value: 'failed', + }, + ], + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['informant.verified'], 'failed') + }) + + await t.step('should map "pending" status', () => { + const registration = buildBirthEventRegistration({ + questionnaire: [ + { + fieldId: 'birth.mother.mother-view-group.verified', + value: 'pending', + }, + ], + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['mother.verified'], 'pending') + }) + + await t.step('should map "failedFetchIdDetails" to "failed"', () => { + const registration = buildBirthEventRegistration({ + questionnaire: [ + { + fieldId: 'birth.mother.mother-view-group.verified', + value: 'failedFetchIdDetails', + }, + ], + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['mother.verified'], 'failed') + }) + + await t.step('should handle undefined verification status', () => { + const registration = buildBirthEventRegistration({ + questionnaire: [], + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['mother.verified'], undefined) + }) + + await t.step('should handle multiple verification fields', () => { + const registration = buildBirthEventRegistration({ + questionnaire: [ + { + fieldId: 'birth.mother.mother-view-group.verified', + value: 'verified', + }, + { + fieldId: 'birth.father.father-view-group.verified', + value: 'authenticated', + }, + { + fieldId: 'birth.informant.informant-view-group.verified', + value: 'pending', + }, + ], + }) + + const result = transform(registration, birthResolver, 'birth') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['mother.verified'], 'verified') + assertEquals(declareAction?.declaration['father.verified'], 'authenticated') + assertEquals(declareAction?.declaration['informant.verified'], 'pending') + }) +}) + +Deno.test('SOM verification status tests - death events', async (t) => { + await t.step('should map "authenticated" status for deceased', () => { + const registration = buildDeathEventRegistration({ + questionnaire: [ + { + fieldId: 'death.deceased.deceased-view-group.verified', + value: 'authenticated', + }, + ], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals( + declareAction?.declaration['deceased.verified'], + 'authenticated' + ) + }) + + await t.step('should map "verified" status for informant', () => { + const registration = buildDeathEventRegistration({ + questionnaire: [ + { + fieldId: 'death.informant.informant-view-group.verified', + value: 'verified', + }, + ], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['informant.verified'], 'verified') + }) + + await t.step('should map "failed" status for spouse', () => { + const registration = buildDeathEventRegistration({ + questionnaire: [ + { + fieldId: 'death.spouse.spouse-view-group.verified', + value: 'failed', + }, + ], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['spouse.verified'], 'failed') + }) + + await t.step('should map "pending" status', () => { + const registration = buildDeathEventRegistration({ + questionnaire: [ + { + fieldId: 'death.deceased.deceased-view-group.verified', + value: 'pending', + }, + ], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['deceased.verified'], 'pending') + }) + + await t.step( + 'should map "failedFetchIdDetails" to "failed" for deceased', + () => { + const registration = buildDeathEventRegistration({ + questionnaire: [ + { + fieldId: 'death.deceased.deceased-view-group.verified', + value: 'failedFetchIdDetails', + }, + ], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['deceased.verified'], 'failed') + } + ) + + await t.step('should handle undefined verification status', () => { + const registration = buildDeathEventRegistration({ + questionnaire: [], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['deceased.verified'], undefined) + }) + + await t.step('should handle multiple verification fields', () => { + const registration = buildDeathEventRegistration({ + questionnaire: [ + { + fieldId: 'death.deceased.deceased-view-group.verified', + value: 'verified', + }, + { + fieldId: 'death.informant.informant-view-group.verified', + value: 'authenticated', + }, + { + fieldId: 'death.spouse.spouse-view-group.verified', + value: 'pending', + }, + ], + }) + + const result = transform(registration, deathResolver, 'death') + const declareAction = result.actions.find((a) => a.type === 'DECLARE') + + assertEquals(declareAction?.declaration['deceased.verified'], 'verified') + assertEquals( + declareAction?.declaration['informant.verified'], + 'authenticated' + ) + assertEquals(declareAction?.declaration['spouse.verified'], 'pending') + }) +})