Skip to content

Commit

Permalink
Merge branch 'master' into validate-docker-helper-version
Browse files Browse the repository at this point in the history
  • Loading branch information
mrjones-plip committed Dec 3, 2024
2 parents f0bab52 + ca0db92 commit cb444db
Show file tree
Hide file tree
Showing 216 changed files with 9,476 additions and 3,112 deletions.
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ https://docs.communityhealthtoolkit.org/contribute/code/workflow/#commit-message

# Code review checklist
<!-- Remove or comment out any items that do not apply to this PR; in the remaining boxes, replace the [ ] with [x]. -->
- [ ] UI/UX backwards compatible: Test it works for the new design (enabled by default). And test it works in the old design, enable `can_view_old_navigation` permission to see the old design.
- [ ] Readable: Concise, well named, follows the [style guide](https://docs.communityhealthtoolkit.org/contribute/code/style-guide/), documented if necessary.
- [ ] Documented: Configuration and user documentation on [cht-docs](https://github.com/medic/cht-docs/)
- [ ] Tested: Unit and/or e2e where appropriate
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/deploy-conf/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ runs:
shell: bash
working-directory: ${{ inputs.directory }}
- name: run cht
run: npx cht --url=https://${{ inputs.username }}:${{ inputs.password }}@${{ inputs.hostname }} compile-app-settings convert-app-forms convert-collect-forms convert-contact-forms upload-app-settings upload-app-forms upload-collect-forms upload-contact-forms upload-resources upload-custom-translations --force
run: npx cht --url=https://${{ inputs.username }}:${{ inputs.password }}@${{ inputs.hostname }} compile-app-settings convert-app-forms convert-collect-forms convert-contact-forms convert-training-forms upload-app-settings upload-app-forms upload-collect-forms upload-contact-forms upload-training-forms upload-resources upload-custom-translations --force
shell: bash
working-directory: ${{ inputs.directory }}
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ env:
TAG: ${{ (github.ref_type == 'tag' && github.ref_name) || '' }}
BRANCH: ${{ github.head_ref || github.ref_name }}
BUILD_NUMBER: ${{ github.run_id }}
NODE_VERSION: '20.11'
NODE_VERSION: '22.11'

jobs:

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Builds brought to you courtesy of GitHub Actions.

## Copyright

Copyright 2013-2022 Medic Mobile, Inc. <[email protected]>
Copyright 2013-2025 Medic Mobile, Inc. <[email protected]>

## License

Expand Down
4 changes: 1 addition & 3 deletions api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
FROM alpine:3.19 AS base_build
FROM node:22-alpine AS base_build

RUN apk add --update --no-cache \
build-base \
curl \
nodejs~=20 \
npm~=10 \
tzdata \
libxslt \
bash \
Expand Down
4 changes: 2 additions & 2 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"url": "git://github.com/medic/cht-core.git"
},
"engines": {
"node": ">=20.11.0",
"npm": ">=10.2.4"
"node": ">=22.11.0",
"npm": ">=10.9.0"
},
"scripts": {
"toc": "doctoc --github --maxlevel 2 README.md",
Expand Down
17 changes: 10 additions & 7 deletions api/resources/translations/messages-en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ configuration.sms = SMS
configuration.sms.forms = SMS forms
configuration.sms.forms.title = You need to choose both an XML file and a Meta file before clicking the upload button. You may only upload one app form file at a time and any existing forms will be overwritten.
configuration.sms.settings = Basic settings
configuration.sms.test.description = Use this page to send a test message to the production application without going through the SMS Gateway. Be sure to use a phone number registered to a CHWs profile to mimic a report coming from him or her about a particular patient.
configuration.sms.test.description = Use this page to send a test message to the production application without going through the SMS Gateway. Be sure to use a phone number registered to a CHW?s profile to mimic a report coming from him or her about a particular patient.
configuration.sms.test.from.number = From phone number
configuration.sms.test.message.description = Limit of 144 characters
configuration.sms.test.number.validation.description = Please enter a valid phone number without dashes or punctuation.
Expand All @@ -454,7 +454,7 @@ confirm.destructive.navigation.submit = Exit
confirm.destructive.navigation.title = Exit form?
confirm.logout = You will need an internet connection to log back in.
password.updated = Your password has been successfully updated.
confirm.verification = This report will be verified as correct. This cannot be changed later.
confirm.verification = This report will be verified as ?correct?. This cannot be changed later.
confirm.verification.submit = Verify as correct
confirm.verification.title = Verify report
contact.age = Age
Expand Down Expand Up @@ -654,8 +654,8 @@ enketo.geopicker.altitude = altitude (m)
enketo.geopicker.closepolygon = close polygon
enketo.geopicker.kmlcoords = KML coordinates
enketo.geopicker.kmlpaste = paste KML coordinates here
enketo.geopicker.latitude = latitude (x.y °)
enketo.geopicker.longitude = longitude (x.y °)
enketo.geopicker.latitude = latitude (x.y °)
enketo.geopicker.longitude = longitude (x.y °)
enketo.geopicker.points = points
enketo.geopicker.searchPlaceholder = search for place or address
enketo.geopicker.removePoint = This will completely remove the current geopoint from the list of geopoints and cannot be undone. Are you sure you want to do this?
Expand Down Expand Up @@ -686,7 +686,7 @@ export.dhis.place.all = All Places
export.dhis.place.description = Filter exported data to include data associated with contacts under this place in the hierarchy.
export.dhis.place.label = Filter by place
export.dhis.unconfigured = DHIS2 integration is not configured.
export.feedback.description = Download a log of detected errors and user feedback submitted via the Report bug feature in CSV format. The table below shows the most recently submitted reports.
export.feedback.description = Download a log of detected errors and user feedback submitted via the ?Report bug? feature in CSV format. The table below shows the most recently submitted reports.
export.messages.description = Download all messages that have ever been sent or received in CSV format.
export.people.description = Download all contacts registered in the system in JSON format.
export.reports.description = Download a summary of all the reports that have ever been submitted in CSV format.
Expand Down Expand Up @@ -1215,7 +1215,7 @@ sync.last_success = Last sync
sync.now = Sync now
sync.retry = Retry
sync.feedback.failure.unknown = Sync failed. Unable to connect.
sync.status.in_progress = Currently syncing
sync.status.in_progress = Currently syncing?
sync.status.not_required = All reports synced
sync.status.required = Reports to sync
sync.status.unknown = Unable to connect
Expand Down Expand Up @@ -1264,7 +1264,10 @@ training_cards.confirm.title = Leave training?
training_cards.error.loading = Error loading training. Please contact your supervisor.
training_cards.error.save = Error saving training.
training_cards.form.saved = Training completed.
training_cards.modal.title = Important changes
training_materials.page.no_more_trainings = No more trainings
training_materials.page.no_selected = No training material selected
training_materials.page.no_trainings = No trainings found
training_materials.page.title = Training materials
translation.add = Add new translation key
translation.key = Translation key
unique.id = Unique ID
Expand Down
5 changes: 4 additions & 1 deletion api/resources/translations/messages-es.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1264,7 +1264,10 @@ training_cards.confirm.title = \¿Salir del entrenamiento\?
training_cards.error.loading = Hubo un error al cargar el entrenamiento. Por favor contacte a su supervisor.
training_cards.error.save = Hubo un error al guardar el entrenamiento.
training_cards.form.saved = Entrenamiento completado.
training_cards.modal.title = Cambios importantes
training_materials.page.no_more_trainings = No hay más entrenamientos
training_materials.page.no_selected = Ningún material de entrenamiento seleccionado
training_materials.page.no_trainings = No se encontraron entrenamientos
training_materials.page.title = Materiales de entrenamiento
translation.add = Agregar Traducción
translation.key = Clave de traducción
unique.id = Identificación única
Expand Down
11 changes: 7 additions & 4 deletions api/resources/translations/messages-fr.properties
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ messages.n.report_accepted = Merci {{contact.name}} d'avoir enregistré {{patien
messages.n.validation.patient_name = {{\#patient_name}} Le format est incorrect, assurez-vous que le message commence avec N suivi d'un espace et du nom de la personne (maximum 30 charactères).{{/patient_name}}{{^patient_name}} Le format est incorrect, assurez-vous que le message commence avec N suivi du nom de la personne.{{/patient_name}}
messages.off.report_accepted = Aucune autre notification concernant {{patient_name}} ne sera envoyée tant que vous n'aurez pas envoyé 'ON {{patient_id}}'. {{\#chw_sms}} {{chw_sms}} {{/chw_sms}}
messages.on.report_accepted = Les notifications pour {{patient_name}} ({{patient_id}}) ont été réactivées. {{\#chw_sms}} {{chw_sms}} {{/chw_sms}}
messages.p.report_accepted = Merci pour avoir enregistré la grossesse
messages.p.report_accepted = Merci pour avoir enregistré la grossesse de {{patient_name}} ({{patient_id}}).{{\#expected_date}} La date prévue pour l'accouchement est {{\#date}}{{expected_date}}{{/date}}{{/expected_date}}
messages.p.validation.last_menstrual_period = Le format d'enregistrement pour {{patient_name}} est incorrect, merci de vous assurer que la dernière menstruation soit un nombre entre 2 et 42.
messages.relay.chw_sms = {{\#chw_sms}}{{chw_sms}}{{/chw_sms}}
messages.schedule.anc.checkin = Où est-ce que {{patient_name}} a acouché? Répondez par 'D {{patient_id}} F' pour un accouchement dans un centre de santé, 'D {{patient_id}} S' pour un accouchement à domicile avec une accoucheuse qualifiée, 'D {{patient_id}} NS pour un accouchement à domicile non qualifié.
Expand Down Expand Up @@ -934,8 +934,8 @@ messages.schedule.postnatal.day_7 = Merci de vérifier si le {{patient_name}} ({
messages.schedule.postnatal.day_7_overdue = Est-ce que le patient {{patient_name}} ({{patient_id}}) est allé à la CPON du 7ème jour ? Confirmez avec 'M {{patient_id}}'. Merci \!
messages.schedule.postnatal.week_6 = Merci de vérifier si le {{patient_name}} ({{patient_id}}) est partie cette semaine pour sa visite postnatale de la semaine - 6. Quand c'est fait, merci de nous le notifier avec 'M {{patient_id}}'. Merci \!
messages.schedule.postnatal.week_6_overdue = Est-ce que {{patient_name}} ({{patient_id}}) est allée pour sa visite de 6eme semaine? Merci de confirmer en envoyant 'M {{patient_id}}'. Merci\!
messages.schedule.registration.followup_anc = Salut {{contact.name}}, n'oubliez de soumettre l'enregistrement de la grossesse pour {{patient_name}} {{patient_id}} avec 'P {{patient_id}} <Weeks since LMP>'. Merci\!
messages.schedule.registration.followup_anc_pnc = {{contact.name}}, est-ce que {{patient_name}} {{patient_id}} a besoin de soins? Pour enregistrer une grossesse envoyer 'P {{patient_id}} <Weeks since LMP>'. Pour les Soins postnataux, soumettre un rapport d'accouchement en envoyant 'D {{patient_id}} <Delivery Code> <Days Since Delivery>'. Merci\!
messages.schedule.registration.followup_anc = Salut {{contact.name}}, n'oubliez de soumettre l'enregistrement de la grossesse pour {{patient_name}} {{patient_id}} avec 'P {{patient_id}} <Semaines depuis les dernières règles>'. Merci\!
messages.schedule.registration.followup_anc_pnc = {{contact.name}}, est-ce que {{patient_name}} {{patient_id}} a besoin de soins? Pour enregistrer une grossesse envoyer 'P {{patient_id}} <Semaines depuis les dernières règles>'. Pour les Soins postnataux, soumettre un rapport d'accouchement en envoyant 'D {{patient_id}} <Code Accouchement> <Jours depuis Accouchement>'. Merci\!
messages.sent.by = Soumis par {{senderName}}
messages.unknown.sender = Émetteur inconnu
messages.v.report_accepted = Merci beaucoup {{contact.name}}, la visite de {{patient_name}} ({{patient_id}}) a été enregistrée.
Expand Down Expand Up @@ -1264,7 +1264,10 @@ training_cards.confirm.title = Quitter l'entraînement?
training_cards.error.loading = Erreur lors du chargement de la formation. Veuillez contacter votre superviseur.
training_cards.error.save = Erreur lors de l'enregistrement de la formation.
training_cards.form.saved = Formation terminée.
training_cards.modal.title = Changements importants
training_materials.page.no_more_trainings = Aucune formation restante
training_materials.page.no_selected = Aucun matériel de formation sélectionné
training_materials.page.no_trainings = Aucune formation trouvée
training_materials.page.title = Matériel de formation
translation.add = Ajouter une traduction
translation.key = Clé de traduction
unique.id = ID unique
Expand Down
4 changes: 4 additions & 0 deletions api/resources/translations/messages-hi.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,10 @@ training_cards.confirm.exit = यह प्रशिक्षण समाप्
training_cards.confirm.button.no = रद्द करें
training_cards.confirm.button.yes = बाहर निकलें
training_cards.confirm.title = प्रशिक्षण छोड़ें?
training_materials.page.no_more_trainings = अब और कोई प्रशिक्षण नहीं है
training_materials.page.no_selected = कोई प्रशिक्षण दस्तावेज़ नहीं चुना गया
training_materials.page.no_trainings = कोई प्रशिक्षण नहीं मिला
training_materials.page.title = प्रशिक्षण दस्तावेज़
translation.add = अनुवाद दर्ज करें
translation.key = अनुवाद का गाइड
unique.id =
Expand Down
4 changes: 4 additions & 0 deletions api/resources/translations/messages-id.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,10 @@ training_cards.confirm.exit = Pelatihan ini belum selesai. Jika Anda keluar seka
training_cards.confirm.button.no = Batalkan
training_cards.confirm.button.yes = Keluar
training_cards.confirm.title = Keluar dari pelatihan?
training_materials.page.no_more_trainings = Tidak ada lagi pelatihan
training_materials.page.no_selected = Tidak ada materi pelatihan yang dipilih
training_materials.page.no_trainings = Tidak ditemukan pelatihan
training_materials.page.title = Materi pelatihan
translation.add = Tambah terjemahan
translation.key = Kunci terjemahan
unique.id =
Expand Down
5 changes: 4 additions & 1 deletion api/resources/translations/messages-ne.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1264,7 +1264,10 @@ training_cards.confirm.title = तालिम छोड्ने हो?
training_cards.error.loading = तालिम लोड गर्दा त्रुटि भयो। तपाइँको सुपरभाइजरलाई सम्पर्क गर्नुहोस्।
training_cards.error.save = तालिम सेभ गर्न त्रुटि।
training_cards.form.saved = तालिम सम्पन्न भयो।
training_cards.modal.title = महत्वपुर्ण परिवर्तनहरु
training_materials.page.no_more_trainings = थप तालिम छैन
training_materials.page.no_selected = कुनै तालिम सामग्री छनोट गरिएको छैन।
training_materials.page.no_trainings = तालिम फेला परेन
training_materials.page.title = तालिम शीर्षक
translation.add = नयाँ अनुवाद कुञ्जी थप्नुहोस्
translation.key = अनुवाद कुञ्जी
unique.id = आईडी
Expand Down
5 changes: 4 additions & 1 deletion api/resources/translations/messages-sw.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1264,7 +1264,10 @@ training_cards.confirm.title = Ungependa kuondoka kwenye mafunzo?
training_cards.error.loading = Hitilafu katika kupakia mafunzo. Tafadhali wasiliana na msimamizi wako.
training_cards.error.save = Hitilafu katika kuhifadhi mafunzo.
training_cards.form.saved = Mafunzo yamekamilika.
training_cards.modal.title = Mabadiliko muhimu
training_materials.page.no_more_trainings = Hakuna mafunzo mengine
training_materials.page.no_selected = Hakuna nyenzo za mafunzo zilizochaguliwa
training_materials.page.no_trainings = Hakuna mafunzo yaliyopatikana
training_materials.page.title = Vifaa vya mafunzo
translation.add = Ongeza tafsiri
translation.key = Ufunguo wa tafsiri
unique.id = Kitambulisho cha kipekee
Expand Down
12 changes: 8 additions & 4 deletions api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,23 +82,27 @@ process
await migrations.run();
logger.info('Database migrations completed successfully');

startupLog.start('forms');
logger.info('Generating manifest');
await manifest.generate();
logger.info('Manifest generated successfully');

logger.info('Generating service worker');
await generateServiceWorker.run(true);
logger.info('Service worker generated successfully');
} catch (err) {
logger.error('Fatal error initialising API');
logger.error('%o', err);
process.exit(1);
}

try {
startupLog.start('forms');
logger.info('Updating xforms…');
await generateXform.updateAll();
logger.info('xforms updated successfully');

} catch (err) {
logger.error('Fatal error initialising API');
logger.error('Error initialising API');
logger.error('%o', err);
process.exit(1);
}

startupLog.complete();
Expand Down
19 changes: 19 additions & 0 deletions api/src/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class PublicError extends Error {
constructor(publicMessage, ...args) {
super(publicMessage, ...args);
this.publicMessage = publicMessage;
}
}

class NotFoundError extends Error {
constructor(message, ...args) {
super(message, ...args);
this.status = 404; // simulate PouchDb error
this.statusCode = 404; // simulate Request error
}
}

module.exports = {
PublicError,
NotFoundError,
};
8 changes: 0 additions & 8 deletions api/src/public-error.js

This file was deleted.

3 changes: 2 additions & 1 deletion api/src/services/privacy-policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const sanitizeHtml = require('sanitize-html');
const db = require('../db');
const logger = require('@medic/logger');
const config = require('../config');
const { NotFoundError } = require('../errors');

const PRIVACY_POLICY_DOC_ID = 'privacy-policies';

Expand All @@ -22,7 +23,7 @@ const getDoc = (options=({})) => {
.then(doc => {
const policies = doc.privacy_policies;
if (!policies || !Object.keys(policies).length) { // invalid doc
throw new Error(`Invalid ${PRIVACY_POLICY_DOC_ID} doc: missing required "privacy_policies" property`);
throw new NotFoundError(`Invalid ${PRIVACY_POLICY_DOC_ID} doc: missing required "privacy_policies" property`);
}
return doc;
})
Expand Down
2 changes: 1 addition & 1 deletion api/src/services/records.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const phoneNumber = require('@medic/phone-number');
const config = require('../config');
const smsparser = require('./report/smsparser');
const validate = require('./report/validate');
const PublicError = require('../public-error');
const { PublicError } = require('../errors');
const DATE_NUMBER_STRING = /(\d{13,})/;

// matches invisible characters that can mess up our parsing
Expand Down
1 change: 0 additions & 1 deletion api/src/services/setup/view-indexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ const indexView = async (dbName, ddocId, viewName) => {
uri: `${environment.serverUrl}/${dbName}/${ddocId}/_view/${viewName}`,
json: true,
qs: { limit: 1 },
timeout: 2000,
});
} catch (requestError) {
if (!continueIndexing) {
Expand Down
5 changes: 0 additions & 5 deletions api/tests/mocha/services/async-storage.spec.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
const sinon = require('sinon');
const rewire = require('rewire');
const { expect } = require('chai');
const asyncHooks = require('node:async_hooks');
const request = require('@medic/couch-request');
const serverUtils = require('../../../src/server-utils');

describe('async-storage', () => {
let service;
let asyncLocalStorage;

beforeEach(() => {
asyncLocalStorage = sinon.spy(asyncHooks, 'AsyncLocalStorage');
sinon.stub(request, 'initialize');
});

Expand All @@ -20,8 +17,6 @@ describe('async-storage', () => {

it('should initialize async storage and initialize couch-request', async () => {
service = rewire('../../../src/services/async-storage');

expect(asyncLocalStorage.callCount).to.equal(1);
expect(request.initialize.args).to.deep.equal([[
service,
serverUtils.REQUEST_ID_HEADER
Expand Down
Loading

0 comments on commit cb444db

Please sign in to comment.