diff --git a/openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-config.json b/openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-config.json deleted file mode 100644 index e49cc74..0000000 --- a/openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "endpoint": "https://app.openfn.org", - "specPath": "openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-spec.yaml", - "statePath": "openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-state.json" -} \ No newline at end of file diff --git a/openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-spec.yaml b/openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-spec.yaml index 7c650f1..00be5de 100644 --- a/openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-spec.yaml +++ b/openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-spec.yaml @@ -13,10 +13,10 @@ credentials: mtuchi@openfn.org-OpenMRS-Demo: name: OpenMRS Demo owner: mtuchi@openfn.org - "mtuchi@openfn.org-OpenMRS-Demo-(HTTP)": + 'mtuchi@openfn.org-OpenMRS-Demo-(HTTP)': name: 'OpenMRS Demo (HTTP)' owner: mtuchi@openfn.org - "mtuchi@openfn.org-googlesheets-(mtuchi@openfn.org)-2": + 'mtuchi@openfn.org-googlesheets-(mtuchi@openfn.org)-2': name: 'googlesheets (mtuchi@openfn.org)-2' owner: mtuchi@openfn.org mtuchi@openfn.org-mtuchi-github-token: @@ -119,7 +119,7 @@ workflows: adaptor: '@openfn/language-msgraph@latest' credential: aleksa@openfn.org-AK-Sharepoint body: | - + // Check out the Job Writing Guide for help getting started: // https://docs.openfn.org/documentation/jobs/job-writing-guide get('sites/root/lists') @@ -142,7 +142,7 @@ workflows: adaptor: '@openfn/language-googlesheets@latest' credential: 'mtuchi@openfn.org-googlesheets-(mtuchi@openfn.org)-2' body: | - + getValues('1OuR7laA7Oc2QnoiT8S3Thhf-HNh7uFY8ILLEu-idXuk', 'optionsets_oct1') @@ -287,13 +287,6 @@ workflows: body: path: workflows/wf2/3-get-encounters.js - Get-Options-Map: - name: Get Options Map - adaptor: '@openfn/language-http@latest' - credential: null - body: - path: workflows/wf2/4-get-options-map.js - Get-TEIs: name: Get TEIs adaptor: '@openfn/language-dhis2@5.0.1' @@ -340,22 +333,16 @@ workflows: condition_label: has-patient-uuids condition_expression: | state.patientUuids.length > 0 && !state.errors - enabled: true - Get-Encounters->Get-Options-Map: + Get-Encounters->Get-TEIs: source_job: Get-Encounters - target_job: Get-Options-Map + target_job: Get-TEIs condition_type: js_expression condition_label: has-encounters condition_expression: | state.encounters.length > 0 - - enabled: true - Get-Options-Map->Get-TEIs: - source_job: Get-Options-Map - target_job: Get-TEIs - condition_type: on_job_success enabled: true + Get-TEIs->Create-Events: source_job: Get-TEIs target_job: Create-Events diff --git a/workflows/wf2/2-mappings.js b/workflows/wf2/2-mappings.js index b8968bb..7cc467f 100644 --- a/workflows/wf2/2-mappings.js +++ b/workflows/wf2/2-mappings.js @@ -1,3 +1,42 @@ +get( + 'https://raw.githubusercontent.com/OpenFn/openfn-lime-pilot/refs/heads/next-staging/metadata/collections.json', + { parseAs: 'json' }, + state => { + const { cursor, lastRunDateTime, patients, data } = state; + + return { + ...data, + cursor, + patients, + lastRunDateTime, + }; + } +); + +// Validates if a string matches UUID v4 format +const isValidUUID = id => { + if (!id || typeof id !== 'string') return false; + + const UUID_PATTERN = + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + return UUID_PATTERN.test(id); +}; + +fn(state => { + const { formMetadata, ...rest } = state; + const formUuids = formMetadata + .filter(form => { + const uuid = form['OMRS form.uuid']; + return isValidUUID(uuid); + }) + .map(form => form['OMRS form.uuid']); + + return { + ...rest, + formUuids, + }; +}); + fn(state => { state.placeOflivingMap = { 'Al Ayadya': 'lon42.423409_lat36.481517', @@ -107,179 +146,6 @@ fn(state => { return state; }); fn(state => { - state.nationalityMap = { - '84066564-253e-43d8-b141-76730cffa878': 'afghanistan', - 'db21f4f9-faf2-4358-8297-0ae76627b3b8': 'albania', - '5f6c017f-074c-46b3-92d0-d055e2094366': 'algeria', - '8a2e5a03-8a74-41ae-9a98-2310f9ce400d': 'angola', - 'c911af8a-171c-4ee9-b1ff-934373e8a819': 'anguilla', - 'b83d24e8-34d8-4920-83c0-8ba014467ff4': 'argentina', - '39f1652a-f2b7-4b65-a7e1-7097ac6cdef0': 'armenia', - 'f3f1cba1-7c1e-4234-86a2-f27bb5964fee': 'aruba', - '29750013-0e35-47ca-8f77-9192a923fb07': 'azerbaijan', - 'a99de53c-ce76-4b1e-91b2-461094baf79e': 'bangladesh', - '664baba4-c552-47b9-97c0-ff67dafd27d6': 'belarus', - 'cf863e31-bb38-48ed-90dd-f3dedcac304c': 'benin', - 'd45a57c9-994f-4deb-8845-9b785860a2ec': 'bhutan', - 'd8800d10-862b-42f1-8e22-cac1ce1bbcae': 'bolivia', - '05d8f4ef-45eb-463d-b2f3-8a5a613ee6b9': 'bosnia_and_herzegovina', - '1304a0de-5b70-4d36-a873-e72a82963316': 'botswana', - '353ff388-64e6-434c-b78f-ca9636390389': 'brazil', - 'b02c6d20-83a2-4947-8a7d-91d1f9c4d8a2': 'british_virgin_islands', - '91c85a62-2b02-483a-aefd-e29d368565fe': 'bulgaria', - '6c90c1ae-17a4-4e94-a267-4fba4c94efd8': 'burkina_faso', - '8a2ed0db-eaad-44bc-bf06-5cb1b2a3db0b': 'burundi', - '4fd14df8-8279-4dfa-bdd3-e1ab26bc0264': 'cambodia', - '873552ac-9850-4cc1-ae09-17eb0fccf405': 'cameroon', - 'cfbc220a-1d6c-4469-bb6d-a8e3deb4f7e7': 'cape_verde', - '61a4c4a4-25c2-4459-a874-ec1d24f8323a': 'central_african_republic', - '9e41e71c-f5d5-456c-a6f9-2129b8055bfc': 'chad', - '05333883-44e9-4f57-836a-041391803007': 'chile', - '15016874-3e20-484a-baa8-9b94e1a3d358': 'china', - 'a008dff8-ce96-4662-bf8a-372e43d424f0': 'colombia', - '9d8738c8-40c2-4c66-aabb-ef176a20ffe8': 'comoros', - 'fb52f8c9-40ec-4dc4-92a4-d465612de2ff': 'costa_rica', - '513cb36a-3f67-46ea-a789-fcdaca0e26f5': 'cote_divoire', - '147c2434-5d7e-420c-8053-ba623301f3f5': 'cuba', - 'cf5b334f-1c0f-41fc-ab54-53ff1e942830': 'djibouti', - 'f70e51e5-b76c-4c38-9bf2-ef8e1f308ce1': 'dominica', - 'ce72fc9b-619b-4c32-b865-600e888ad814': 'dominican_republic', - '8f6d3d2a-e09f-473b-99c9-e539f97ceab6': 'drc_congo', - '854f2f66-40e1-4a6a-9dee-09c832a52289': 'east_timor', - 'f9810f9a-78a7-42a2-99e6-19c629642386': 'ecuador', - 'cc7343f8-9243-4d09-b378-58363850d624': 'egypt', - '9a34935e-5a8a-45be-8ccd-cb23192e420f': 'el_salvador', - '7e591605-d723-4398-982a-8737af63a2dc': 'equatorial_guinea', - 'c61f03c2-0d1f-444f-a974-0a61063aff71': 'eritrea', - '7478d375-014e-410e-a355-090143e88f5b': 'ethiopia', - '9f46ae06-114a-47fa-8f8d-e9749f04da25': 'gabon', - '5ec7ddd7-14a5-48ec-9e7c-8896d1010655': 'gambia', - 'bc71788f-db69-4b6f-8d1c-57a74395bdd2': 'georgia', - '2eb4ff46-d908-4148-9b0d-40ccfc1a655a': 'ghana', - 'b75d6bcc-fadf-4141-8d0f-2463154b89f7': 'greece', - '51fa502b-98a3-4c42-b5fd-7b4d64489bb9': 'guam', - '3725a4d2-b28f-466b-905a-bafeaeb75855': 'guatemala', - 'eeaff39c-8afd-43f7-b9a0-53729f5df1d8': 'guinea', - '14e90203-9197-42ea-9222-acafd2fd6984': 'guinea_bissau', - 'ba4dfa7e-f3cd-4e94-8ca7-6b96a93378a8': 'guyana', - 'f76f7dcb-f82e-4257-a627-1685ff3f3586': 'haiti', - 'cdd1336e-495b-4868-aace-57a84442d6fd': 'honduras', - '378d0107-eb43-485d-930c-0704b4e5aa11': 'india', - '1cbe17e6-adc2-4680-bee0-54d94af75ebf': 'indonesia', - 'b422270e-d8af-4a32-b523-742545a17a3f': 'iran', - '03aa7d6e-7656-48e4-8dc0-5e27706722c0': 'iraq', - '842f963c-f84d-4076-a8db-337295fd9b91': 'jordan', - '6f6d0e78-2c81-411d-8d13-367e250dc110': 'kazakhstan', - 'ad351a33-8846-4cad-8195-b07b6041d4a5': 'kenya', - '4aee7a88-cda9-454e-9f25-4a6420270417': 'kuwait', - 'ace3b851-042b-46a6-8fea-68aae042d614': 'kyrgyzstan', - 'dfb01b39-c224-459e-b045-dd9461b9a1e5': 'laos', - '7de78f22-f53e-48d2-923f-ae1e4d814f46': 'lebanon', - 'b35b29c5-9bb7-4b40-ad33-29eecd28a9e6': 'lesotho', - 'a5fd61b4-fd27-433d-8428-7e88a7f27921': 'liberia', - '600c6af4-b767-423c-b942-7f06ca467258': 'libya', - '5837cc40-9ab5-4088-91c8-ca6e4b57e903': 'macedonia', - '3782bf3c-380e-4b60-b21a-38199073f112': 'madagascar', - 'e8b5f188-6a5c-43ae-b4a5-200abb13153e': 'malawi', - '3facca11-fbaa-4c40-8fac-4751d45c3f1b': 'malaysia', - '3e844a47-526a-46f9-afea-1af9ff8690aa': 'mali', - '051ce04e-05e8-4430-8b75-3e499bbffbc8': 'martinique', - '8acb006b-8596-4a98-8177-acb4cb575956': 'mauritania', - '17ced083-eb2a-4046-a713-26cabc7af95d': 'mauritius', - '1af148fe-2698-4b89-bf7f-87e5c48b6848': 'mayotte', - '8381208f-01ca-4ed3-8f2c-f73ed1c316e3': 'mexico', - 'f0e9c8b5-69b5-48df-8cb9-2d089ba04e46': 'moldova', - '9f341cb1-dcb5-4f6c-bd21-b57db01b4193': 'mongolia', - 'ef467a17-91e8-4124-a136-7ed8ff7c7d15': 'morocco', - '0916133b-4d93-4d60-9c20-e7ee3936f391': 'mozambique', - 'e81ba700-f9fc-4ed0-b248-578a25717cdb': 'myanmar', - '0cb123dc-8810-4840-b6ab-6a527c5a79ef': 'namibia', - '3386fe63-2158-4040-a502-9f65fd2079d3': 'nauru', - 'fb01b01a-6775-423c-8012-7d43f587cb6c': 'nepal', - 'e67c072b-7707-491f-8c2e-13c914216b61': 'new_caledonia', - 'f6a9521c-596b-49f9-b914-67138e8c17e6': 'nicaragua', - '7561db90-a866-4443-93f4-95cac1d47e9c': 'niger', - '4134651a-7f53-45fb-8bc6-7fed9cf36f51': 'nigeria', - '4d3079e4-8568-48e6-9342-665896875a38': 'north_korea', - '9b0af037-99d1-43b8-ac06-82137ec4b06d': 'oman', - Other: 'other', - 'f45d93c3-c9b0-4333-a5e6-299b7c425812': 'pakistan', - 'e2a19948-49aa-44c0-98ef-67ae1160ef43': 'palestine', - '1ef5a828-9d0d-4336-91ab-880d5dc0151c': 'panama', - 'e1e6b451-d7fe-4954-b225-99b2de82a4c0': 'papua_new_guinea', - 'd8412016-82f5-4801-a026-1bdc429850b7': 'paraguay', - 'e74fa87f-8469-46b0-975f-6cb37c394564': 'peru', - 'bdbd5c9f-1f28-4f4d-a254-4a84f8bb2c8f': 'philippines', - '39fca1d0-d2e7-4b13-82bd-626fbec71252': 'puerto_rico', - '5db9afa5-b57e-4f45-8b1c-af766f14fc58': 'republic_of_congo', - '1dae4b2d-50c9-4bf1-b25a-7063600a5e74': 'reunion', - '457e745e-ae97-463d-95a9-8d5689d3ca2b': 'romania', - '6bc925a1-7699-496a-85b0-c290699381db': 'rwanda', - 'e03b381b-a7f4-40eb-964f-51571dc3c48c': 'samoa', - 'f66bbb42-684f-42d7-bfcd-95d586eb7dc9': 'sao_tome_and_principe', - 'fdf495a4-e60c-46f7-a8a2-61a216849086': 'saudi_arabia', - 'ad948f1b-0733-4f8d-b049-d64289b43a10': 'senegal', - 'd2e69cef-3bff-4220-ba91-a6a678fb606b': 'serbia', - 'ffba9caf-b6aa-4078-845e-578f7a7fd566': 'sierra_leone', - '99c8dccc-4dfa-4d30-86be-42a309ab431f': 'somalia', - '75882d62-1c55-480d-b411-8ca40c3307df': 'south_africa', - '0603d6b9-334f-4443-ab60-7c5d457b95fc': 'south_korea', - 'f113e24e-2ea9-49a2-9b28-59241b9adb21': 'south_sudan', - 'b0031c01-d242-4410-b98b-cc1511590b85': 'sri_lanka', - '2f03a932-2b75-4e8b-9f44-0fcd83c75dc4': 'sudan', - 'c65d3329-98d4-4dd7-89d3-141b70d00eb2': 'suriname', - '06a2703b-af17-4e44-83f5-6cc9a8a75320': 'swaziland', - 'dcdcdc70-a006-4b0a-bac2-7de89b022b65': 'switzerland', - '1e34ee55-ef9f-4386-bae6-6995555ded75': 'syria', - '34836c60-5449-48d6-b3c9-c0b3361b9f2c': 'tajikistan', - '050a8eb1-0d77-4f65-a2da-776a13bcd2a4': 'tanzania', - '289ac5bd-6434-4837-86bf-b54d22970ac8': 'thailand', - '6a583e64-869d-477d-a1c1-746320d45fc4': 'togo', - 'd381f06d-2365-4f40-948b-cfe90d8cb532': 'tonga', - '56be7864-fde6-4db3-8fa5-b9dd42cd9c53': 'tunisia', - '7429c779-1d3a-4aec-8256-d0b1637e1bd1': 'turkey', - '3ef17df5-299b-4385-9ea6-572df4b6f9ca': 'turkmenistan', - 'be3d11d3-446d-440c-a582-d01c7cbb0eda': 'uganda', - '38c99c8d-2b93-4848-a537-b1865a260bb2': 'ukraine', - Unknown: 'unknown', - 'c2e45baf-748b-4d7b-a391-ed6b802b6f94': 'uruguay', - '60512350-d79b-41aa-aff0-1b28ca4aa5f1': 'uzbekistan', - '557cea4a-0049-4b7a-b373-ed63f294a2a0': 'venezuela', - '49509c5f-e533-48a8-bf06-86935e3376b2': 'vietnam', - '4086dfd2-f4f5-4107-93e8-07bee235af8f': 'western_sahara', - '6a3214e0-f94b-414c-8148-968e24386671': 'yemen', - '3ec0432d-ea37-4159-a658-29d6f07fe21a': 'zambia', - 'ce1b0d8d-0a2d-4f93-a6ed-64aca2fd0f45': 'zimbabwe', - }; - - state.statusMap = { - '18692c24-4d33-4cdc-a92c-bf4138da5d6d': 'unknown', //Inconnu - 'f921ffdd-72ca-4d58-a89b-1fa2e959d110': 'asylum_seeker', - '2bacead2-f280-457c-9d28-e80e106f7d25': 'no_status', - 'MSF-AAAAAA000000000000001929': 'refugee', - '1067AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA': 'unknown', - '20b8524e-4c26-4fa0-81f0-fa23ebacc54d': 'single', - 'MSF-AAAAAA000000000000001863': 'married', - 'MSF-AAAAAA000000000000001864': 'widowed', - 'MSF-AAAAAA000000000000001865': 'divorced_separated', - '1060AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA': 'concubine', - 'MSF-AAAAAA000000000000001823': 'not_applicable', - '1067AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA': 'unknown', - 'MSF-AAAAAA000000000000001871': 'student', - '4a18a820-f3a1-4bb7-9138-558a9ecc81da': 'fixed_employee', - '2cb73bee-7f94-4695-89c7-c81187dbc90c': 'occasional_employee', - 'MSF-AAAAAA000000000000001870': 'unemployed', - '9b14b4d4-b749-4acf-acfe-79c480f3c4b3': 'housewife', - 'MSF-AAAAAA000000000000001823': 'not_applicable', - 'MSF-AAAAAA000000000000001329': 'unknown', - '1067AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA': 'unknown', - 'MSF-AAAAAA000000000000001930': 'idp', - '515c5abe-4172-4d0c-a991-0de2888228d7': 'internationally_displaced', - 'bbdb287c-4ba1-4944-bd87-eb126c5f9d92': 'non_displaced', - 'fc49acaa-ece2-4365-9dfb-70c2105de8b1': 'returnee', - '1067AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA': 'unknown', - }; - state.genderOptions = { M: 'male', F: 'female', diff --git a/workflows/wf2/2-upsert-teis.js b/workflows/wf2/2-upsert-teis.js index ffd39f2..63d5350 100644 --- a/workflows/wf2/2-upsert-teis.js +++ b/workflows/wf2/2-upsert-teis.js @@ -1,7 +1,11 @@ const buildPatientsUpsert = (state, patient, isNewPatient) => { - const { nationalityMap, statusMap, placeOflivingMap, genderOptions } = state; - const DHIS2_PATIENT_NUMBER = '8d79403a-c2cc-11de-8d13-0010c6dffd0f'; //DHIS2 ID or DHIS2 Patient Number - const OPENMRS_AUTO_ID = '05a29f94-c0ed-11e2-94be-8c13b969e334'; //MSF ID or OpenMRS Patient Number + const { placeOflivingMap, genderOptions } = state; + const DHIS2_PATIENT_NUMBER = state.identifiers.find( + i => i.type === 'DHIS2_PATIENT_NUMBER' + )?.['omrs identifierType']; //DHIS2 ID or DHIS2 Patient Number + const OPENMRS_AUTO_ID = state.identifiers.find( + i => i.type === 'OPENMRS_AUTO_ID' + )?.['omrs identifierType']; //MSF ID or OpenMRS Patient Number const dateCreated = patient.auditInfo.dateCreated.substring(0, 10); const findIdentifierByUuid = (identifiers, targetUuid) => identifiers.find(i => i.identifierType.uuid === targetUuid)?.identifier; @@ -15,6 +19,24 @@ const buildPatientsUpsert = (state, patient, isNewPatient) => { }, ]; + const findOptsUuid = uuid => + patient.person.attributes.find(a => a.attributeType.uuid === uuid)?.value + ?.uuid; + + const findOptCode = optUuid => + state.optsMap.find(o => o['value.uuid - External ID'] === optUuid)?.[ + 'DHIS2 Option Code' + ]; + + const patientMap = state.formMaps.patient.dataValueMap; + const statusAttrMaps = Object.keys(patientMap).map(d => { + const optUid = findOptsUuid(patientMap[d]); + return { + attribute: d, + value: findOptCode(optUid), + }; + }); + const payload = { query: { ou: 'OPjuJMZFLop', @@ -64,73 +86,13 @@ const buildPatientsUpsert = (state, patient, isNewPatient) => { attribute: 'rBtrjV1Mqkz', //Place of living value: placeOflivingMap[patient.person?.addresses[0]?.cityVillage], }, - { - attribute: 'Xvzc9e0JJmp', //nationality - value: - nationalityMap[ - patient.person.attributes.find( - a => - a.attributeType.uuid === - '24d1fa23-9778-4a8e-9f7b-93f694fc25e2' - )?.value?.uuid - ], //input.attributeType = "24d1fa23-9778-4a8e-9f7b-93f694fc25e2" - }, - { - attribute: 'YUIQIA2ClN6', //current status - value: - statusMap[ - patient.person.attributes.find( - a => - a.attributeType.uuid === - 'e0b6ed99-72c4-4847-a442-e9929eac4a0f' - )?.value?.uuid - ], //input.attributeType = "e0b6ed99-72c4-4847-a442-e9929eac4a0f" - }, - // TODO: Qq6xQ2s6LO8 has an error, Aleksa to ask the client - { - attribute: 'Qq6xQ2s6LO8', //legal status - value: - statusMap[ - patient.person.attributes.find( - a => - a.attributeType.uuid === - 'a9b2c642-097f-43f8-b96b-4d2f50ffd9b1' - )?.value?.uuid - ], //input.attributeType = "a9b2c642-097f-43f8-b96b-4d2f50ffd9b1" - }, - { - attribute: 'FpuGAOu6itZ', //marital status - value: - statusMap[ - patient.person.attributes.find( - a => - a.attributeType.uuid === - '3884dc76-c271-4bcb-8df8-81c6fb897f53' - )?.value?.uuid - ], //input.attributeType = "3884dc76-c271-4bcb-8df8-81c6fb897f53" - }, - { - attribute: 'v7k4OcXrWR8', //employment status - value: - statusMap[ - patient.person.attributes.find( - a => - a.attributeType.uuid === - 'dd1f7f0f-ccea-4228-9aa8-a8c3b0ea4c3e' - )?.value?.uuid - ], //input.attributeType = "dd1f7f0f-ccea-4228-9aa8-a8c3b0ea4c3e" - }, - { - attribute: 'SVoT2cVLd5O', //Number of children - value: patient.person.attributes.find( - a => a.attributeType.uuid === 'e363161a-9d5c-4331-8463-238938f018ed' - )?.value, //input.attributeType = "e363161a-9d5c-4331-8463-238938f018ed" - }, + ...statusAttrMaps, ], }, }; - console.log('mapped dhis2 payloads:: ', JSON.stringify(payload, null, 2)); + // TODO: AK do we need this log👇🏾? + // console.log('mapped dhis2 payloads:: ', JSON.stringify(payload, null, 2)); if (isNewPatient) { console.log('create enrollment'); @@ -143,7 +105,7 @@ const buildPatientsUpsert = (state, patient, isNewPatient) => { const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); each( - '$.patients[*]', + $ => $.patients.slice(0, 1), get( 'tracker/trackedEntities', { @@ -171,7 +133,11 @@ each( // Upsert TEIs to DHIS2 each( $.patientsUpsert, - upsert('trackedEntityInstances', $.data.query, $.data.data) + upsert('trackedEntityInstances', $.data.query, state => { + // Uncomment👇🏾 for inspecting input payload + // console.log('Upserting', state.data.data); + return state.data.data; + }) ); fn(state => { const { @@ -179,11 +145,10 @@ fn(state => { response, references, patients, - statusMap, patientsUpsert, - nationalityMap, placeOflivingMap, genderOptions, + identifiers, ...next } = state; diff --git a/workflows/wf2/3-get-encounters.js b/workflows/wf2/3-get-encounters.js index c920a35..4f12728 100644 --- a/workflows/wf2/3-get-encounters.js +++ b/workflows/wf2/3-get-encounters.js @@ -1,17 +1,10 @@ -const formUuids = [ - '82db23a1-4eb1-3f3c-bb65-b7ebfe95b19b', - '6a3e1e0e-dd13-3465-b8f5-ee2d42691fe5', - 'be8c12f9-e6fd-369a-9bc7-46a191866f15', - '48577ac5-d9c0-3000-9bac-075409b38336', - 'ee6b1b06-3163-334a-8538-be69250af727', -]; // Fetch patient encounters then filter by cursor date // OpenMRS demo instance does not support querying ALL records (q=all) each( '$.patientUuids[*]', getEncounters({ patient: $.data, v: 'full' }, state => { const patientUuid = state.references.at(-1); - const filteredEncounters = formUuids.map(formUuid => + const filteredEncounters = state.formUuids.map(formUuid => state.data.results.filter( encounter => encounter.encounterDatetime >= state.cursor && diff --git a/workflows/wf2/4-get-options-map.js b/workflows/wf2/4-get-options-map.js deleted file mode 100644 index 221d04c..0000000 --- a/workflows/wf2/4-get-options-map.js +++ /dev/null @@ -1,48 +0,0 @@ -// state.mhgapClosure = { //Waiting on form -// yUT7HyjWurN: 'encounter-date', // encounterDate -// }; -get( - 'https://raw.githubusercontent.com/OpenFn/openfn-lime-pilot/refs/heads/main/metadata/metadata_mapping.json', - { parseAs: 'json' } -); - -fn(({ data, response, references, ...next }) => { - const { - optionSets, - f01MhpssBaseline, - f02MhpssFollowUp, - f03MhgapBaseline, - f04MhgapFollowUp, - f05MhpssClosure, - } = data; - - next.optionSets = optionSets; - next.formMaps = { - '6a3e1e0e-dd13-3465-b8f5-ee2d42691fe5': { - //formName: mhpss baseline - programStage: 'MdTtRixaC1B', - dataValueMap: f01MhpssBaseline, - }, - '82db23a1-4eb1-3f3c-bb65-b7ebfe95b19b': { - ////formName: mhgap baseline - programStage: 'EZJ9FsNau7Q', - dataValueMap: f03MhgapBaseline, - }, - 'be8c12f9-e6fd-369a-9bc7-46a191866f15': { - //formName: mhpss followup - programStage: 'eUCtSH80vMe', - dataValueMap: f02MhpssFollowUp, - }, - '48577ac5-d9c0-3000-9bac-075409b38336': { - //formName: mhgap followup - programStage: 'hjHwYnSfJnX', - dataValueMap: f04MhgapFollowUp, - }, - 'ee6b1b06-3163-334a-8538-be69250af727': { - //formName: mhpss closure - programStage: 'xrCTheIzyDV', - dataValueMap: f05MhpssClosure, - }, - }; - return next; -}); diff --git a/workflows/wf2/6-create-events.js b/workflows/wf2/6-create-events.js index 061b809..6652cfe 100644 --- a/workflows/wf2/6-create-events.js +++ b/workflows/wf2/6-create-events.js @@ -1,15 +1,15 @@ -const processAnswer = (answer, conceptUuid, dataElement, optionSets) => { +const processAnswer = (answer, conceptUuid, dataElement, optsMap) => { // console.log('Has answer', conceptUuid, dataElement); return typeof answer.value === 'object' - ? processObjectAnswer(answer, conceptUuid, dataElement, optionSets) + ? processObjectAnswer(answer, conceptUuid, dataElement, optsMap) : processOtherAnswer(answer, conceptUuid, dataElement); }; -const processObjectAnswer = (answer, conceptUuid, dataElement, optionSets) => { +const processObjectAnswer = (answer, conceptUuid, dataElement, optsMap) => { if (isDiagnosisByPsychologist(conceptUuid, dataElement)) { return '' + answer.value.uuid === '278401ee-3d6f-4c65-9455-f1c16d0a7a98'; } - return findMatchingOption(answer, optionSets); + return findMatchingOption(answer, optsMap); }; const processOtherAnswer = (answer, conceptUuid, dataElement) => { @@ -27,8 +27,8 @@ const processNoAnswer = (data, conceptUuid, dataElement) => { return ''; }; -const findMatchingOption = (answer, optionSets) => { - const matchingOption = optionSets.find( +const findMatchingOption = (answer, optsMap) => { + const matchingOption = optsMap.find( o => o['value.uuid - External ID'] === answer.value.uuid )?.['DHIS2 Option Code']; @@ -59,13 +59,13 @@ const getRangePhq = input => { return '0_4'; }; -const dataValuesMapping = (data, dataValueMap, optionSets) => { +const dataValuesMapping = (data, dataValueMap, optsMap) => { return Object.keys(dataValueMap) .map(dataElement => { const conceptUuid = dataValueMap[dataElement]; const answer = data.obs.find(o => o.concept.uuid === conceptUuid); const value = answer - ? processAnswer(answer, conceptUuid, dataElement, optionSets) + ? processAnswer(answer, conceptUuid, dataElement, optsMap) : processNoAnswer(data, conceptUuid, dataElement); return { dataElement, value }; @@ -91,7 +91,7 @@ fn(state => { return { ...event, programStage: form.programStage, - dataValues: dataValuesMapping(data, form.dataValueMap, state.optionSets), + dataValues: dataValuesMapping(data, form.dataValueMap, state.optsMap), }; } }); diff --git a/workflows/wf2/workflow.json b/workflows/wf2/workflow.json index 7052f5a..4f75725 100644 --- a/workflows/wf2/workflow.json +++ b/workflows/wf2/workflow.json @@ -15,7 +15,7 @@ }, { "id": "mappings", - "adaptor": "common", + "adaptor": "http", "expression": "2-mappings.js", "next": { "upsert-teis": "!state.errors" @@ -36,15 +36,7 @@ "configuration": "../tmp/openmrs-creds.json", "expression": "3-get-encounters.js", "next": { - "get-options-map": "state.encounters.length > 0&& !state.errors" - } - }, - { - "id": "get-options-map", - "adaptor": "http", - "expression": "4-get-options-map.js", - "next": { - "get-teis": "!state.errors" + "get-teis": "!state.errors && state.encounters.length > 0" } }, { diff --git a/workflows/wf3/1-fetch-metadata.js b/workflows/wf3/1-fetch-metadata.js index f74517c..aaaa358 100644 --- a/workflows/wf3/1-fetch-metadata.js +++ b/workflows/wf3/1-fetch-metadata.js @@ -1,19 +1,5 @@ -const toCamelCase = text => { - return text - .toLowerCase() - .replace(/[^a-zA-Z0-9]+(.)/g, (match, chr) => chr.toUpperCase()); -}; - -const sheets = [ - 'OptionSets', - 'F01-MHPSS Baseline', - 'F02-MHPSS Follow-up', - 'F03-mhGAP Baseline', - 'F04-mhGAP Follow-up', - 'F05-MHPSS Closure', -]; - fn(state => { + state.sheets = ['OptionSets', 'identifiers']; state.siteId = 'openfnorg.sharepoint.com,4724a499-afbc-4ded-a371-34ae40bf5d8d,1d20a7d4-a6f1-407c-aa77-76bd47bb0f32'; return state; @@ -38,10 +24,31 @@ fn(state => { return state; }); +get( + `${$.workbookBase}/worksheets('omrs-form-metadata')/usedRange`, + {}, + state => { + const [headers, ...rows] = state.data.values.slice(0); + state.formMetadata = rows + .map(row => + row.reduce((obj, value, index) => { + if (value != null && value !== '') { + obj[headers[index]] = value; + } + return obj; + }, {}) + ) + .filter(obj => Object.keys(obj).length > 0); + + state.sheets.push(...state.formMetadata.map(obj => obj['OMRS form name'])); + return state; + } +); + each( - sheets, + $.sheets, get(`${$.workbookBase}/worksheets('${$.data}')/usedRange`, {}, state => { - const sheetName = toCamelCase(state.references.at(-1)); + const sheetName = state.references.at(-1); console.log('Fetched sheet: ', sheetName); state[sheetName] = state.data.values; return state; diff --git a/workflows/wf3/2-map-metadata.js b/workflows/wf3/2-map-metadata.js index 583dd60..5c29493 100644 --- a/workflows/wf3/2-map-metadata.js +++ b/workflows/wf3/2-map-metadata.js @@ -6,13 +6,33 @@ const mapArrayToObject = (item, keys) => { return acc; }, {}); }; -fn(state => { - const { optionsets } = state; - const keys = optionsets[1]; - const optsMap = optionsets.slice(2).map(item => mapArrayToObject(item, keys)); +const safeKeyValuePairs = arr => { + if (arr === null || arr === undefined) { + return arr; + } + const mappedArr = arr.slice(2).map(item => mapArrayToObject(item, arr[1])); + try { + return mappedArr + .filter( + o => isValidValue(o['External ID']) && isValidValue(o['DHIS2 DE UID']) + ) + .reduce((acc, value) => { + acc[value['DHIS2 DE UID']] = value['External ID']; + return acc; + }, {}); + } catch (error) { + console.error(`Error processing ${arr}:`, error); + return arr; // Return original value if processing fails + } +}; + +fn(state => { + const { OptionSets, identifiers } = state; + const keys = OptionSets[1]; - state.optionSets = optsMap + state.optsMap = OptionSets.slice(2) + .map(item => mapArrayToObject(item, keys)) .filter( o => isValidValue(o['External ID']) && isValidValue(o['DHIS2 DE full name']) @@ -31,51 +51,35 @@ fn(state => { }; }); + const [iheaders, ...irows] = identifiers; + state.identifiers = irows + .map(row => + row.reduce((obj, value, index) => { + if (value != null && value !== '') { + obj[iheaders[index]] = value; + } + return obj; + }, {}) + ) + .filter(obj => Object.keys(obj).length > 0); return state; }); -const safeKeyValuePairs = arr => { - if (arr === null || arr === undefined) { - return arr; - } - const mappedArr = arr.slice(2).map(item => mapArrayToObject(item, arr[1])); - try { - return mappedArr - .filter( - o => isValidValue(o['External ID']) && isValidValue(o['DHIS2 DE UID']) - ) - .reduce((acc, value) => { - acc[value['DHIS2 DE UID']] = value['External ID']; - return acc; - }, {}); - } catch (error) { - console.error(`Error processing ${arr}:`, error); - return arr; // Return original value if processing fails - } -}; - -fn( - ({ - optionSets, - f01MhpssBaseline, - f02MhpssFollowUp, - f03MhgapBaseline, - f04MhgapFollowUp, - f05MhpssClosure, - }) => { - const processedState = Object.fromEntries( - Object.entries({ - f01MhpssBaseline, - f02MhpssFollowUp, - f03MhgapBaseline, - f04MhgapFollowUp, - f05MhpssClosure, - }).map(([key, value]) => [key, safeKeyValuePairs(value)]) - ); +fn(state => { + const { formMetadata, optsMap, identifiers } = state; - return { - optionSets, - ...processedState, + const formMaps = formMetadata.reduce((acc, form) => { + const formName = form['OMRS form name']; + acc[form['OMRS form.uuid']] = { + formName, + orgUnit: form['DHIS2 orgUnit ID'], + programId: form['DHIS2 program ID'], + programStage: form['DHIS2 programStage ID'], + dataValueMap: safeKeyValuePairs(state[formName]), }; - } -); + + return acc; + }, {}); + + return { formMaps, formMetadata, optsMap, identifiers }; +}); diff --git a/workflows/wf3/3-save-options.js b/workflows/wf3/3-save-options.js index 304e504..c25fbd9 100644 --- a/workflows/wf3/3-save-options.js +++ b/workflows/wf3/3-save-options.js @@ -1,22 +1,17 @@ const metadataPath = - 'repos/OpenFn/openfn-lime-pilot/contents/metadata/metadata_mapping.json'; + 'repos/OpenFn/openfn-lime-pilot/contents/metadata/collections.json'; get(metadataPath, { headers: { 'user-agent': 'OpenFn', }, + query: { + ref: 'next-staging', + }, }); fn(state => { - const { - optionSets, - f01MhpssBaseline, - f02MhpssFollowUp, - f03MhgapBaseline, - f04MhgapFollowUp, - f05MhpssClosure, - data, - } = state; + const { formMaps, formMetadata, optsMap, data, identifiers } = state; state.body = { message: 'Update metadata content', @@ -26,15 +21,14 @@ fn(state => { }, content: util.encode( JSON.stringify({ - optionSets, - f01MhpssBaseline, - f02MhpssFollowUp, - f03MhgapBaseline, - f04MhgapFollowUp, - f05MhpssClosure, + optsMap, + formMaps, + identifiers, + formMetadata, }) ), sha: data.sha, + branch: 'next-staging', }; return state;