diff --git a/openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-state.json b/openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-state.json index b248689..5159b0c 100644 --- a/openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-state.json +++ b/openfn-cd92dd57-9a3c-4318-bdcb-f57a386cf811-state.json @@ -3,7 +3,7 @@ "name": "msf-lime-iraq", "description": null, "inserted_at": "2024-09-05T06:59:05Z", - "updated_at": "2024-09-18T12:48:06Z", + "updated_at": "2024-09-19T05:02:55Z", "scheduled_deletion": null, "project_credentials": { "mtuchi@openfn.org-OpenMRS-Demo": { @@ -80,26 +80,26 @@ }, "concurrency": null, "inserted_at": "2024-09-06T11:58:48Z", - "updated_at": "2024-09-18T12:48:06Z", + "updated_at": "2024-09-19T05:02:55Z", "jobs": { "Get-Patients": { "id": "ab326112-9cdd-4449-8611-b5abc659d4ca", "name": "Get Patients", - "body": "// here we define the date cursor\nfn(state => {\n //manualCursor at beggining of the project 2023-05-20T06:01:24.000+0000\n const manualCursor = '2023-07-27T07:16:24.544Z';\n\n state.cursor = state.lastRunDateTime || manualCursor;\n\n console.log(\n 'Date cursor to filter & get only recent OMRS records ::',\n state.cursor\n );\n\n return state;\n});\n\nsearchPatient({ q: 'IQ', v: 'full', limit: '100' });\n//searchPatient({ q: 'MHtwo', v: 'full', limit: '3' });\n// searchPatient({ q: 'Patient', v: 'full', limit: '100' });\n//Query all patients (q=all) not supported on demo OpenMRS; needs to be configured\n//...so we query all Patients with name \"Patient\" instead\n\nfn(state => {\n const { results } = state.data;\n\n const getPatientByUuid = uuid =>\n results.find(patient => patient.uuid === uuid).auditInfo.dateCreated;\n\n // console.log('dateCreated for patient uuid ...2c6dbfc5acc8',getPatientByUuid(\"31b4d9c8-f7cc-4c26-ae61-2c6dbfc5acc8\"))\n //console.log(JSON.stringify(state.data, null, 2));\n\n console.log('Filtering patients to only sync most recent records...');\n\n state.patients = results.filter(\n patient =>\n (patient.auditInfo.dateChanged === null\n ? patient.auditInfo.dateCreated\n : patient.auditInfo.dateChanged) > state.cursor\n );\n console.log('# of patients to sync to dhis2 ::', state.patients.length);\n console.log('uuids of patients to sync to dhis2 ::', state.patients.map(p => p.uuid));\n // console.log(JSON.stringify(patients, null, 2));\n\n state.lastRunDateTime = new Date().toISOString();\n console.log('Updating cursor; next sync start date:', state.lastRunDateTime);\n\n state.data = {};\n state.response = {};\n state.references = [];\n return state;\n});\n", + "body": "// here we define the date cursor\nfn(state => {\n //manualCursor at beggining of the project 2023-05-20T06:01:24.000+0000\n const manualCursor = '2023-07-27T07:16:24.544Z';\n\n state.cursor = state.lastRunDateTime || manualCursor;\n\n console.log(\n 'Date cursor to filter & get only recent OMRS records ::',\n state.cursor\n );\n\n return state;\n});\n\nsearchPatient({ q: 'Katrina', v: 'full', limit: '100' });\n//Query all patients (q=all) not supported on demo OpenMRS; needs to be configured\n//...so we query all Patients with name \"Patient\" instead\n\nfn(state => {\n const { results } = state.data;\n\n const getPatientByUuid = uuid =>\n results.find(patient => patient.uuid === uuid).auditInfo.dateCreated;\n\n //console.log('dateCreated for patient uuid ...2c6dbfc5acc8',getPatientByUuid(\"31b4d9c8-f7cc-4c26-ae61-2c6dbfc5acc8\"))\n //console.log(JSON.stringify(state.data, null, 2));\n\n console.log('Filtering patients to only sync most recent records...');\n\n state.patients = results.filter(\n patient =>\n (patient.auditInfo.dateChanged === null\n ? patient.auditInfo.dateCreated\n : patient.auditInfo.dateChanged) > state.cursor\n );\n console.log('# of patients to sync to dhis2 ::', state.patients.length);\n console.log(\n 'uuids of patients to sync to dhis2 ::',\n state.patients.map(p => p.uuid)\n );\n\n state.lastRunDateTime = new Date().toISOString();\n console.log('Updating cursor; next sync start date:', state.lastRunDateTime);\n\n state.data = {};\n state.response = {};\n state.references = [];\n return state;\n});\n", "adaptor": "@openfn/language-openmrs@latest", "project_credential_id": "3141d874-5456-4168-9680-ce04efb1089c" }, "Upsert-TEIs": { "id": "6973c510-b36d-4c42-82f5-b26d8cd36a57", "name": "Upsert TEIs", - "body": "fn(state => {\n const { nationalityMap, statusMap, placeOflivingMap, genderOptions } = state;\n\n const DHIS2_PATIENT_NUMBER = '8d79403a-c2cc-11de-8d13-0010c6dffd0f'; //DHIS2 ID or DHIS2 Patient Number\n const OPENMRS_AUTO_ID = '05a29f94-c0ed-11e2-94be-8c13b969e334'; //MSF ID or OpenMRS Patient Number\n const patientsUpsert = [];\n\n const buildPatientsUpsert = (patient, isNewPatient) => {\n const dateCreated = patient.auditInfo.dateCreated.substring(0, 10);\n\n function findIdentifierByUuid(identifiers, targetUuid) {\n // Use the `find` method to locate the matching identifier\n const matchingIdentifier = identifiers.find(\n identifier => identifier.identifierType.uuid === targetUuid\n );\n\n // Return the `identifier` value if a match is found; otherwise, return null\n return matchingIdentifier ? matchingIdentifier.identifier : undefined;\n }\n\n const calculateDOB = age => {\n const currentDate = new Date();\n const currentYear = currentDate.getFullYear();\n const birthYear = currentYear - age;\n\n const birthday = new Date(\n birthYear,\n currentDate.getMonth(),\n currentDate.getDay()\n );\n\n return birthday.toISOString().replace(/\\.\\d+Z$/, '+0000');\n };\n\n const enrollments = [\n {\n orgUnit: 'OPjuJMZFLop',\n program: 'w9MSPn5oSqp',\n programStage: 'MdTtRixaC1B',\n enrollmentDate: dateCreated,\n },\n ];\n\n const payload = {\n query: {\n ou: 'OPjuJMZFLop',\n program: 'w9MSPn5oSqp',\n filter: [`AYbfTPYMNJH:Eq:${patient.uuid}`], //upsert on omrs.patient.uid\n },\n data: {\n program: 'w9MSPn5oSqp',\n orgUnit: 'OPjuJMZFLop',\n trackedEntityType: 'cHlzCA2MuEF',\n attributes: [\n {\n attribute: 'fa7uwpCKIwa',\n value: patient.person?.names[0]?.givenName,\n },\n {\n attribute: 'Jt9BhFZkvP2',\n value: patient.person?.names[0]?.familyName,\n },\n {\n attribute: 'P4wdYGkldeG', //DHIS2 ID ==> \"Patient Number\"\n value:\n findIdentifierByUuid(patient.identifiers, DHIS2_PATIENT_NUMBER) ||\n findIdentifierByUuid(patient.identifiers, OPENMRS_AUTO_ID), //map OMRS ID if no DHIS2 id\n },\n {\n attribute: 'ZBoxuExmxcZ', //MSF ID ==> \"OpenMRS Patient Number\"\n value: findIdentifierByUuid(patient.identifiers, OPENMRS_AUTO_ID),\n },\n {\n attribute: 'AYbfTPYMNJH', //\"OpenMRS Patient UID\"\n value: patient.uuid,\n },\n {\n attribute: 'qptKDiv9uPl',\n value: genderOptions[patient.person.gender],\n },\n {\n attribute: 'Rv8WM2mTuS5',\n value: patient.person.age,\n },\n {\n attribute: 'WDp4nVor9Z7',\n value: patient.person.birthdate.slice(0, 10),\n },\n {\n attribute: 'rBtrjV1Mqkz', //Place of living\n value: placeOflivingMap[patient.person?.addresses[0]?.cityVillage],\n },\n {\n attribute: 'Xvzc9e0JJmp', //nationality\n value:\n nationalityMap[\n patient.person.attributes.find(\n a =>\n a.attributeType.uuid ===\n '24d1fa23-9778-4a8e-9f7b-93f694fc25e2'\n )?.value.uuid\n ], //input.attributeType = \"24d1fa23-9778-4a8e-9f7b-93f694fc25e2\"\n },\n {\n attribute: 'YUIQIA2ClN6', //current status\n value:\n statusMap[\n patient.person.attributes.find(\n a =>\n a.attributeType.uuid ===\n 'e0b6ed99-72c4-4847-a442-e9929eac4a0f'\n )?.value.uuid\n ], //input.attributeType = \"e0b6ed99-72c4-4847-a442-e9929eac4a0f\"\n },\n {\n attribute: 'Qq6xQ2s6LO8', //legal status\n value:\n statusMap[\n patient.person.attributes.find(\n a =>\n a.attributeType.uuid ===\n 'a9b2c642-097f-43f8-b96b-4d2f50ffd9b1'\n )?.value.uuid\n ], //input.attributeType = \"a9b2c642-097f-43f8-b96b-4d2f50ffd9b1\"\n },\n {\n attribute: 'FpuGAOu6itZ', //marital status\n value:\n statusMap[\n patient.person.attributes.find(\n a =>\n a.attributeType.uuid ===\n '3884dc76-c271-4bcb-8df8-81c6fb897f53'\n )?.value.uuid\n ], //input.attributeType = \"3884dc76-c271-4bcb-8df8-81c6fb897f53\"\n },\n {\n attribute: 'v7k4OcXrWR8', //employment status\n value:\n statusMap[\n patient.person.attributes.find(\n a =>\n a.attributeType.uuid ===\n 'dd1f7f0f-ccea-4228-9aa8-a8c3b0ea4c3e'\n )?.value.uuid\n ], //input.attributeType = \"dd1f7f0f-ccea-4228-9aa8-a8c3b0ea4c3e\"\n },\n {\n attribute: 'SVoT2cVLd5O', //Number of children\n value: patient.person.attributes.find(\n a =>\n a.attributeType.uuid === 'e363161a-9d5c-4331-8463-238938f018ed'\n )?.value, //input.attributeType = \"e363161a-9d5c-4331-8463-238938f018ed\"\n },\n ],\n },\n };\n\n console.log('mapped dhis2 payloads:: ', JSON.stringify(payload, null, 2));\n\n if (isNewPatient) {\n console.log('create enrollment');\n payload.data.enrollments = enrollments;\n }\n\n return patientsUpsert.push(payload);\n };\n\n return {\n ...state,\n genderOptions,\n patientsUpsert,\n buildPatientsUpsert,\n };\n});\n\nconst delay = ms => new Promise(resolve => setTimeout(resolve, ms));\n\neach(\n '$.patients[*]',\n get(\n 'trackedEntityInstances',\n {\n ou: 'OPjuJMZFLop',\n filter: [`AYbfTPYMNJH:Eq:${$.data.uuid}`],\n program: 'w9MSPn5oSqp',\n },\n {},\n async state => {\n const patient = state.references.at(-1);\n console.log(patient.uuid, 'patient uuid');\n const { trackedEntityInstances } = state.data;\n const isNewPatient = trackedEntityInstances.length === 0;\n\n state.buildPatientsUpsert(patient, isNewPatient);\n await delay(2000);\n return state;\n }\n )\n);\n\n// Upsert TEIs to DHIS2\neach(\n '$.patients[*]',\n get(\n 'tracker/trackedEntities',\n {\n orgUnit: 'OPjuJMZFLop',\n filter: [`AYbfTPYMNJH:Eq:${$.data.uuid}`],\n program: 'w9MSPn5oSqp',\n },\n {},\n async state => {\n const patient = state.references.at(-1);\n console.log(patient.uuid, 'patient uuid');\n const { instances } = state.data;\n const isNewPatient = instances.length === 0;\n\n state.buildPatientsUpsert(patient, isNewPatient);\n await delay(2000);\n return state;\n }\n )\n);\n\n// Upsert TEIs to DHIS2\neach(\n 'patientsUpsert[*]',\n upsert('trackedEntityInstances', $.data.query, $.data.data)\n);\n\nfn(state => {\n const {\n data,\n patients,\n statusMap,\n references,\n patientsUpsert,\n nationalityMap,\n placeOflivingMap,\n genderOptions,\n response,\n ...next\n } = state;\n return next;\n});\n", + "body": "fn(state => {\n const { nationalityMap, statusMap, placeOflivingMap, genderOptions } = state;\n\n const DHIS2_PATIENT_NUMBER = '8d79403a-c2cc-11de-8d13-0010c6dffd0f'; //DHIS2 ID or DHIS2 Patient Number\n const OPENMRS_AUTO_ID = '05a29f94-c0ed-11e2-94be-8c13b969e334'; //MSF ID or OpenMRS Patient Number\n const patientsUpsert = [];\n\n const buildPatientsUpsert = (patient, isNewPatient) => {\n const dateCreated = patient.auditInfo.dateCreated.substring(0, 10);\n\n function findIdentifierByUuid(identifiers, targetUuid) {\n // Use the `find` method to locate the matching identifier\n const matchingIdentifier = identifiers.find(\n identifier => identifier.identifierType.uuid === targetUuid\n );\n\n // Return the `identifier` value if a match is found; otherwise, return null\n return matchingIdentifier ? matchingIdentifier.identifier : undefined;\n }\n\n const calculateDOB = age => {\n const currentDate = new Date();\n const currentYear = currentDate.getFullYear();\n const birthYear = currentYear - age;\n\n const birthday = new Date(\n birthYear,\n currentDate.getMonth(),\n currentDate.getDay()\n );\n\n return birthday.toISOString().replace(/\\.\\d+Z$/, '+0000');\n };\n\n const enrollments = [\n {\n orgUnit: 'OPjuJMZFLop',\n program: 'w9MSPn5oSqp',\n programStage: 'MdTtRixaC1B',\n enrollmentDate: dateCreated,\n },\n ];\n\n const payload = {\n query: {\n ou: 'OPjuJMZFLop',\n program: 'w9MSPn5oSqp',\n filter: [`AYbfTPYMNJH:Eq:${patient.uuid}`], //upsert on omrs.patient.uid\n },\n data: {\n program: 'w9MSPn5oSqp',\n orgUnit: 'OPjuJMZFLop',\n trackedEntityType: 'cHlzCA2MuEF',\n attributes: [\n {\n attribute: 'fa7uwpCKIwa',\n value: patient.person?.names[0]?.givenName,\n },\n {\n attribute: 'Jt9BhFZkvP2',\n value: patient.person?.names[0]?.familyName,\n },\n {\n attribute: 'P4wdYGkldeG', //DHIS2 ID ==> \"Patient Number\"\n value:\n findIdentifierByUuid(patient.identifiers, DHIS2_PATIENT_NUMBER) ||\n findIdentifierByUuid(patient.identifiers, OPENMRS_AUTO_ID), //map OMRS ID if no DHIS2 id\n },\n {\n attribute: 'ZBoxuExmxcZ', //MSF ID ==> \"OpenMRS Patient Number\"\n value: findIdentifierByUuid(patient.identifiers, OPENMRS_AUTO_ID),\n },\n {\n attribute: 'AYbfTPYMNJH', //\"OpenMRS Patient UID\"\n value: patient.uuid,\n },\n {\n attribute: 'qptKDiv9uPl',\n value: genderOptions[patient.person.gender],\n },\n {\n attribute: 'Rv8WM2mTuS5',\n value: patient.person.age,\n },\n {\n attribute: 'WDp4nVor9Z7',\n value: patient.person.birthdate.slice(0, 10),\n },\n {\n attribute: 'rBtrjV1Mqkz', //Place of living\n value: placeOflivingMap[patient.person?.addresses[0]?.cityVillage],\n },\n {\n attribute: 'Xvzc9e0JJmp', //nationality\n value:\n nationalityMap[\n patient.person.attributes.find(\n a =>\n a.attributeType.uuid ===\n '24d1fa23-9778-4a8e-9f7b-93f694fc25e2'\n )?.value.uuid\n ], //input.attributeType = \"24d1fa23-9778-4a8e-9f7b-93f694fc25e2\"\n },\n {\n attribute: 'YUIQIA2ClN6', //current status\n value:\n statusMap[\n patient.person.attributes.find(\n a =>\n a.attributeType.uuid ===\n 'e0b6ed99-72c4-4847-a442-e9929eac4a0f'\n )?.value.uuid\n ], //input.attributeType = \"e0b6ed99-72c4-4847-a442-e9929eac4a0f\"\n },\n {\n attribute: 'Qq6xQ2s6LO8', //legal status\n value:\n statusMap[\n patient.person.attributes.find(\n a =>\n a.attributeType.uuid ===\n 'a9b2c642-097f-43f8-b96b-4d2f50ffd9b1'\n )?.value.uuid\n ], //input.attributeType = \"a9b2c642-097f-43f8-b96b-4d2f50ffd9b1\"\n },\n {\n attribute: 'FpuGAOu6itZ', //marital status\n value:\n statusMap[\n patient.person.attributes.find(\n a =>\n a.attributeType.uuid ===\n '3884dc76-c271-4bcb-8df8-81c6fb897f53'\n )?.value.uuid\n ], //input.attributeType = \"3884dc76-c271-4bcb-8df8-81c6fb897f53\"\n },\n {\n attribute: 'v7k4OcXrWR8', //employment status\n value:\n statusMap[\n patient.person.attributes.find(\n a =>\n a.attributeType.uuid ===\n 'dd1f7f0f-ccea-4228-9aa8-a8c3b0ea4c3e'\n )?.value.uuid\n ], //input.attributeType = \"dd1f7f0f-ccea-4228-9aa8-a8c3b0ea4c3e\"\n },\n {\n attribute: 'SVoT2cVLd5O', //Number of children\n value: patient.person.attributes.find(\n a =>\n a.attributeType.uuid === 'e363161a-9d5c-4331-8463-238938f018ed'\n )?.value, //input.attributeType = \"e363161a-9d5c-4331-8463-238938f018ed\"\n },\n ],\n },\n };\n\n console.log('mapped dhis2 payloads:: ', JSON.stringify(payload, null, 2));\n\n if (isNewPatient) {\n console.log('create enrollment');\n payload.data.enrollments = enrollments;\n }\n\n return patientsUpsert.push(payload);\n };\n\n return {\n ...state,\n genderOptions,\n patientsUpsert,\n buildPatientsUpsert,\n };\n});\n\nconst delay = ms => new Promise(resolve => setTimeout(resolve, ms));\n\neach(\n '$.patients[*]',\n get(\n 'trackedEntityInstances',\n {\n ou: 'OPjuJMZFLop',\n filter: [`AYbfTPYMNJH:Eq:${$.data.uuid}`],\n program: 'w9MSPn5oSqp',\n },\n {},\n async state => {\n const patient = state.references.at(-1);\n console.log(patient.uuid, 'patient uuid');\n const { trackedEntityInstances } = state.data;\n const isNewPatient = trackedEntityInstances.length === 0;\n\n state.buildPatientsUpsert(patient, isNewPatient);\n await delay(2000);\n return state;\n }\n )\n);\n\n// Upsert TEIs to DHIS2\neach(\n '$.patients[*]',\n get(\n 'tracker/trackedEntities',\n {\n orgUnit: 'OPjuJMZFLop',\n filter: [`AYbfTPYMNJH:Eq:${$.data.uuid}`],\n program: 'w9MSPn5oSqp',\n },\n {},\n async state => {\n const patient = state.references.at(-1);\n console.log(patient.uuid, 'patient uuid');\n const { instances } = state.data;\n const isNewPatient = instances.length === 0;\n\n state.buildPatientsUpsert(patient, isNewPatient);\n await delay(2000);\n return state;\n }\n )\n);\n\n// Upsert TEIs to DHIS2\neach(\n 'patientsUpsert[*]',\n upsert('trackedEntityInstances', $.data.query, $.data.data)\n);\n\nfn(state => {\n const {\n data,\n patients,\n statusMap,\n references,\n patientsUpsert,\n nationalityMap,\n placeOflivingMap,\n genderOptions,\n response,\n ...next\n } = state;\n\n next.patientUuids = patients.map(p => p.uuid);\n return next;\n});\n", "adaptor": "@openfn/language-dhis2@5.0.1", "project_credential_id": "8a5ead9b-5f9e-49b1-9e9a-9dc3c4ccf72d" }, "Get-Encounters": { "id": "8fb577e5-d068-4d47-8172-81f08153ced9", "name": "Get Encounters", - "body": "// Fetch encounters from the date of cursor\n// OpenMRS demo instance does not support querying ALL records (q=all)\n// getEncounters({ q: 'Patient', v: 'full', limit: 100 });\ngetEncounters({ q: 'Katrina', v: 'full' });\n\n// Update cursor and return encounters\nfn(state => {\n const { cursor, data } = state;\n\n console.log('cursor datetime::', cursor);\n console.log('Filtering encounters to only get recent records...');\n // console.log(\n // 'Encounters returned before we filter for most recent ::',\n // JSON.stringify(encountersFound, null, 2)\n // );\n state.formUuids = [\n '82db23a1-4eb1-3f3c-bb65-b7ebfe95b19b',\n '6a3e1e0e-dd13-3465-b8f5-ee2d42691fe5',\n ];\n\n const encountersFound = state.formUuids.map(formUuid =>\n data.results.filter(\n encounter =>\n encounter.encounterDatetime >= cursor && encounter.form.uuid == formUuid\n )\n );\n\n state.encounters = encountersFound\n .map(encounters => encounters[0])\n .filter(e => e);\n\n console.log(\n '# of new encounters found in OMRS ::',\n encountersFound.flat().length\n );\n\n console.log(\n '# of new encounters to sync to dhis2 ::',\n state.encounters.length\n );\n\n return { ...state, data: {}, response: {}, references: [] };\n});\n", + "body": "fn(state => {\n state.formUuids = [\n '82db23a1-4eb1-3f3c-bb65-b7ebfe95b19b',\n '6a3e1e0e-dd13-3465-b8f5-ee2d42691fe5',\n ];\n\n console.log('cursor datetime::', state.cursor);\n\n return state;\n});\n\n// Fetch patient encounters then filter by cursor date\n// OpenMRS demo instance does not support querying ALL records (q=all)\neach(\n '$.patientUuids[*]',\n getEncounters({ patient: $.data, v: 'full' }, state => {\n const patientUuid = state.references.at(-1);\n const filteredEncounters = state.formUuids.map(formUuid =>\n state.data.results.filter(\n encounter =>\n encounter.encounterDatetime >= state.cursor &&\n encounter.form.uuid == formUuid\n )\n );\n\n state.encounters ??= [];\n state.encounters.push(\n filteredEncounters.map(encounters => encounters[0]).filter(e => e)\n );\n\n console.log(\n filteredEncounters.flat().length,\n `# of filtered encounters found in OMRS for ${patientUuid}`\n );\n\n return state;\n })\n);\n\n// Log filtered encounters\nfn(state => {\n const {\n encounters,\n data,\n response,\n references,\n index,\n patientUuids,\n ...next\n } = state;\n next.encounters = encounters.flat();\n console.log(next.encounters.length, '# of new encounters to sync to dhis2');\n\n return next;\n});\n", "adaptor": "@openfn/language-openmrs@4.0.0", "project_credential_id": "3141d874-5456-4168-9680-ce04efb1089c" }, @@ -133,7 +133,7 @@ } }, "deleted_at": null, - "lock_version": 139, + "lock_version": 140, "triggers": { "cron": { "enabled": false,