Skip to content

Commit

Permalink
Merge #6337
Browse files Browse the repository at this point in the history
6337: Feat: merge ODK central work r=jmcameron a=jniles

This PR adds in [ODK Central](https://docs.getodk.org/central-install/) functionality to the BHIMA server.  Here is what the ODK administration interface looks like:

![image](https://user-images.githubusercontent.com/896472/150951277-34cf068b-9eca-4363-a485-ad135ad18df1.png)

You'll need to set your own username/password and ODK Central URL to test it out.

This installation is very bare-bones.  The way this works is somewhat documented in #6235 (comment).

This PR is plucked from work done by `@jniles` and `@mbayopanda` in `sprint-prosani`.

Closes #6235.
Closes #6302.
Closes #6322.

---

**TESTING**  (based on session with `@jniles` )
- Use bhima test (rebuild db, etc)
- Go to Administration > ODK Settings
  - Enter the ODK Central login credentials in the left-most panel (ODK Integration Settings)
  - In the middle panel, click on
    - Synchronize enterprise settings
    - Synchronize users
    - Synchronize forms
- Go to Adminstration > User Management
   - In the action menu for Superuser, click on the [ODK QR Code] link to display the QR code (leave it up on your screen)
- On your mobile phone
  - If you have not installed ODK Collect, do that
  - In ODK Collect, click on the [Configure with QR code] button on the entry screen. This should bring up the "Test Enterprise" page
  - Click on [Get Blank Form].  It should show that it has downloaded a form
  - On the bottom right of the page, click on the [Get Selected] button.  This downloads the form and sends you back to the "Test Enterprise" page.
  - Click on [Fill Blank Form] and select the form.  This should start filling out the form.
  - Fill out the form
     - For Nombre des articles, enter 1
     - For the barcode, first go to Bhima (Stock Lots) and display the barcode for lot VITAMIN-A.  Then scan that barcode with your phone.  When you click next, it should show the lot you selected.
     - Finish the form ([X] Mark form as finalize, press [Save Form and Exit])
  - Back on the "Test Enterprise" page, click [Send Finalized Form].  Select the form you just completed and press [Send Selected] on the bottom right of the page.
  - Back on Bhima go to Administration > ODK Settings
  - Click on:  Synchronize Submissions [Sync]
  - Go to Stock > Stock Movements and verify that you have a new stock entry in Depot Tertiaire







Co-authored-by: Jonathan Niles <jonathanwniles@gmail.com>
  • Loading branch information
bors[bot] and jniles authored Jan 28, 2022
2 parents 39ceb33 + e6d9174 commit 37e0c07
Show file tree
Hide file tree
Showing 25 changed files with 1,088 additions and 89 deletions.
6 changes: 4 additions & 2 deletions client/src/i18n/en/form.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"CANCEL_EDIT": "Cancel Edit",
"CASHBOX_MANAGEMENT": "Cashbox Management",
"CLEAR": "Clear",
"SYNC" : "Sync",
"CLEAR_GRID_CONFIGURATION": "Clear Grid Configuration",
"CLOSE": "Close",
"COLLAPSE_RECORDS" : "Collapse Records",
Expand Down Expand Up @@ -552,8 +553,7 @@
"LAST_NAME": "Last Name",
"LAST_PAGE": "Last Page Visited",
"LATITUDE": "Latitude",
"LEGEND" : "Legend",
"LEVEL_OF_STUDY" : "Level of study",
"LEVEL_OF_STUDY" : "Level of study",
"LIMIT": "Limit",
"LIST_STRUCTURE": "List structure",
"LOCATION_REGISTER": "Location Register",
Expand Down Expand Up @@ -911,6 +911,7 @@
"CASHBOX": "Enter the cashbox",
"CHOICE_FILTER": "Choice Filter",
"CODE": "Enter code",
"CONDITION": "Condition",
"COUNTRY": "Enter country",
"CREDITOR": "Enter creditor",
"CURRENCY": "Select currency",
Expand All @@ -920,6 +921,7 @@
"DEFAULT_PURCHASE_ORDER_INTERVAL": "The default purchase order interval for calculating Maximum stock",
"DESCRIPTION": "Enter description",
"DOCUMENT_NAME": "Enter document name",
"ORIGIN": "Origin",
"PATIENT_GROUP": "Enter patient group",
"EMAIL": "Enter email",
"LOT":"Enter Batch Number",
Expand Down
26 changes: 26 additions & 0 deletions client/src/i18n/en/odk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"ODK" : {
"CONFIG_SETTINGS" : "ODK Central Configuration",
"NO_CONFIGURATION_SET" : "There is no ODK Central configuration set up. To get started, add a username, password, and the ODK Central URL.",
"IMPORTED_SUCCESSFULLY" : "Records successfully imported",
"LOAD_FOSA_DATA" : "Load data of FOSA",
"NO_RECORD_FOUND" : "No record found",
"ODK_ADMIN_PASSWORD" : "ODK Central Admin Password",
"ODK_ADMIN_USER" : "ODK Central Admin Email",
"ODK_CENTRAL_URL" : "ODK Central Server URL",
"ODK_INTEGRATION_SETTINGS" : "ODK Integration Settings",
"PROJECT_DETAILS" : "Project Details",
"PROJECT_FORMS" : "Number of Forms",
"PROJECT_ID" : "Project ID",
"PROJECT_NAME" : "Project Name",
"QRCODE" : "ODK QR Code",
"SHOW_QRCODE" : "Show QR Code",
"SYNC_ENTERPRISE" : "Synchronize enterprise settings",
"SYNC_FORMS" : "Synchronize forms",
"SYNC_SETTINGS" : "Synchronization Settings",
"SYNC_SUBMISSIONS" : "Synchronize submissions",
"SYNC_USERS" : "Synchronize users",
"TOTAL_APP_USERS" : "Number of App Users",
"TOTAL_FOUND" : "Total records found"
}
}
1 change: 1 addition & 0 deletions client/src/i18n/en/tree.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
"STOCK_REPORT" : "Stock Inventories",
"STOCK_REQUISITION":"Requisition",
"STOCK_SETTINGS": "Stock Settings",
"ODK_SETTINGS" : "ODK Settings",
"STOCK" : "Stock",
"STOCK_VALUE" : "Stock Value Report",
"SUBSIDY" : "Subsidy Management",
Expand Down
6 changes: 4 additions & 2 deletions client/src/i18n/fr/form.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"BACK": "Précédent",
"BREAKDOWNS_PERCENTAGES": "Ventilation en pourcentage",
"CANCEL": "Annuler",
"SYNC" : "Synchroniser",
"CANCEL_EDIT": "Annuler Modifier",
"CASHBOX_MANAGEMENT": "Gestion des Caisses",
"CLEAR": "Effacer",
Expand Down Expand Up @@ -556,8 +557,7 @@
"LAST_NAME": "Nom",
"LAST_PAGE": "La dernière page visitée",
"LATITUDE": "Latitude",
"LEGEND" : "Legende",
"LEVEL_OF_STUDY" : "Niveau d'étude",
"LEVEL_OF_STUDY" : "Niveau d'étude",
"LIMIT": "Limite",
"LIST_STRUCTURE": "Liste",
"LOCATION_REGISTER": "Enregistrer une localisation",
Expand Down Expand Up @@ -913,6 +913,7 @@
"CHOICE_FILTER": "Filtre",
"CODE": "Entrer le code",
"COUNTRY": "Entrer le nom du pays",
"CONDITION": "Condition",
"CREDITOR": "Entrer un Créditeur",
"CURRENCY": "Sélectionner la monnaie",
"DEBTOR": "Entrer un Débiteur",
Expand All @@ -929,6 +930,7 @@
"MAX_CREDIT": "Entrer le crédit maximale",
"NAME": "Entrer le nom",
"NOTES": "Commentaire",
"ORIGIN": "Origine",
"PASSWORD": "Entrer le mot de passe",
"PATIENT_ID": "Entrer identifiant patient",
"PATIENT_NAME": "Entrer nom patient",
Expand Down
26 changes: 26 additions & 0 deletions client/src/i18n/fr/odk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"ODK" : {
"CONFIG_SETTINGS" : "ODK Central Configuration",
"NO_CONFIGURATION_SET" : "Aucune configuration ODK Central n'est configurée. Pour commencer, ajoutez un nom d'utilisateur, un mot de passe et l'URL d'ODK Central.",
"IMPORTED_SUCCESSFULLY" : "Données importées avec succès",
"LOAD_FOSA_DATA" : "Chargement des données des FOSA",
"NO_RECORD_FOUND" : "Aucun enregistrement trouvé",
"ODK_ADMIN_PASSWORD" : "Mot de passe de l'administrateur",
"ODK_ADMIN_USER" : "Adressse mail de l'administrateur",
"ODK_CENTRAL_URL" : "URL du serveur ODK Central",
"ODK_INTEGRATION_SETTINGS" : "Paramètres d'intégration ODK",
"PROJECT_DETAILS" : "Détails du projet",
"PROJECT_FORMS" : "Nombre de formulaire",
"PROJECT_ID" : "ID Projet",
"PROJECT_NAME" : "Nom du projet",
"QRCODE" : "ODK QR Code",
"SHOW_QRCODE" : "Afficher QR Code",
"SYNC_ENTERPRISE" : "Synchroniser les paramètres d'entreprise",
"SYNC_FORMS" : "Synchroniser les formulaires",
"SYNC_SETTINGS" : "Synchroniser les paramètres",
"SYNC_SUBMISSIONS" : "Synchroniser les soumissions des formulaires",
"SYNC_USERS" : "Synchroniser les utilisateurs",
"TOTAL_APP_USERS" : "Nombre des utilisateurs mobiles",
"TOTAL_FOUND" : "Total des enregistrements trouvés"
}
}
1 change: 1 addition & 0 deletions client/src/i18n/fr/tree.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
"STOCK_REQUISITION":"Réquisition",
"STOCK_MOVEMENT_REPORT": "Rapport graphique des mouvements",
"STOCK_SETTINGS": "Paramètres de stock",
"ODK_SETTINGS" : "Paramètres de l'ODK",
"SUBSIDY":"Gestion des subventions",
"TAGS" : "Gestion des étiquettes",
"TRANSACTION_TYPE":"Types de Transaction",
Expand Down
142 changes: 142 additions & 0 deletions client/src/modules/odk-settings/odk-settings.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<div class="flex-header">
<div class="bhima-title">
<ol class="headercrumb">
<li class="static" translate>TREE.ADMIN</li>
<li class="title" translate>TREE.ODK_SETTINGS</li>
</ol>
</div>
</div>

<div class="flex-content">
<div class="container-fluid">
<div class="col-xs-12 col-md-6 col-lg-4">
<form
name="ODKSettingsForm"
bh-submit="ODKSettingsCtrl.submit(ODKSettingsForm)"
ng-model-options="{ updateOn: 'blur' }" novalidate>
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-server"></i>
<span translate>ODK.ODK_INTEGRATION_SETTINGS</span>
</div>
<div class="panel-body">
<div class="form-group" ng-class="{ 'has-error' : ODKSettingsForm.$submitted && ODKSettingsForm.odk_central_url.$invalid }">
<label class="control-label" translate>ODK.ODK_CENTRAL_URL</label>
<input
type="text"
class="form-control"
name="odk_central_url"
ng-model="ODKSettingsCtrl.settings.odk_central_url"
autocomplete="off">
<div class="help-block" ng-messages="ODKSettingsForm.odk_central_url.$error" ng-show="ODKSettingsForm.$submitted">
<div ng-messages-include="modules/templates/messages.tmpl.html"></div>
</div>
</div>

<div class="form-group" ng-class="{ 'has-error' : ODKSettingsForm.$submitted && ODKSettingsForm.odk_admin_user.$invalid }">
<label class="control-label" translate>ODK.ODK_ADMIN_USER</label>
<input
type="email"
class="form-control"
name="odk_admin_user"
ng-model="ODKSettingsCtrl.settings.odk_admin_user"
autocomplete="off"
ng-required="!!ODKSettingsCtrl.settings.odk_central_url">
<div class="help-block" ng-messages="ODKSettingsForm.odk_admin_user.$error" ng-show="ODKSettingsForm.$submitted">
<div ng-messages-include="modules/templates/messages.tmpl.html"></div>
</div>
</div>

<div class="form-group" ng-class="{ 'has-error' : ODKSettingsForm.$submitted && ODKSettingsForm.odk_admin_password.$invalid }">
<label class="control-label" translate>ODK.ODK_ADMIN_PASSWORD</label>
<input
type="password"
class="form-control"
name="odk_admin_password"
ng-model="ODKSettingsCtrl.settings.odk_admin_password"
autocomplete="off"
ng-required="!!ODKSettingsCtrl.settings.odk_admin_user">
<div class="help-block" ng-messages="ODKSettingsForm.odk_admin_password.$error" ng-show="ODKSettingsForm.$submitted">
<div ng-messages-include="modules/templates/messages.tmpl.html"></div>
</div>
</div>
</div>
<div class="panel-footer text-right">
<bh-loading-button loading-state="ODKSettingsForm.$loading">
<span translate>FORM.BUTTONS.UPDATE</span>
</bh-loading-button>
</div>
</div>
</div>
</form>


<div class="col-xs-12 col-md-6 col-lg-4">
<div class="panel panel-danger">
<div class="panel-heading">
<i class="fa fa-cloud-upload"></i>
<span translate>ODK.SYNC_SETTINGS</span>
</div>

<div class="panel-body">
<fieldset ng-disabled="ODKSettingsCtrl.loading">
<div class="form-group">
<label class="control-label" translate>ODK.SYNC_ENTERPRISE</label>
<button type="button" class="btn btn-default btn-xs pull-right" ng-click="ODKSettingsCtrl.syncEnterprise()" translate>FORM.BUTTONS.SYNC</button>
</div>

<hr>

<div class="form-group">
<label class="control-label" translate>ODK.SYNC_USERS</label>
<button type="button" class="btn btn-default btn-xs pull-right" ng-click="ODKSettingsCtrl.syncAppUsers()" translate>FORM.BUTTONS.SYNC</button>
</div>


<hr>

<div class="form-group">
<label class="control-label" translate>ODK.SYNC_FORMS</label>
<button type="button" class="btn btn-default btn-xs pull-right" ng-click="ODKSettingsCtrl.syncForms()" translate>FORM.BUTTONS.SYNC</button>
</div>

<hr>

<div class="form-group">
<label class="control-label" translate>ODK.SYNC_SUBMISSIONS</label>
<button type="button" class="btn btn-default btn-xs pull-right" ng-click="ODKSettingsCtrl.syncSubmissions()" translate>FORM.BUTTONS.SYNC</button>
</div>
</fieldset>
</div>
</div>
</div>

<div class="col-xs-12 col-md-6 col-lg-4">
<div class="panel panel-success">
<div class="panel-heading">
<i class="fa fa-cogs"></i>
<span translate>ODK.CONFIG_SETTINGS</span>
</div>

<div class="panel-body">
<div ng-show="ODKSettingsCtrl.loading" class="text-center">
<loading-indicator></loading-indicator>
</div>

<dl ng-hide="ODKSettingsCtrl.loading && ODKSettingsCtrl.hasODKConfiguration">
<dt translate>ODK.PROJECT_DETAILS</dt>
<dd><span translate>ODK.PROJECT_NAME</span>: {{ODKSettingsCtrl.project.name}} </dd>
<dd><span translate>ODK.PROJECT_ID</span>: {{ODKSettingsCtrl.project.id}} </dd>
<dd><span translate>ODK.PROJECT_FORMS</span>: {{ODKSettingsCtrl.project.forms}} </dd>
<dd><span translate>FORM.LABELS.CREATED</span>: {{ODKSettingsCtrl.project.createdAt | date}} </dd>
<dd><span translate>ODK.TOTAL_APP_USERS</span>: {{ODKSettingsCtrl.appUsers.length | number}} </dd>
</dl>

<p ng-if="!ODKSettingsCtrl.loading && !ODKSettingsCtrl.hasODKConfiguration" translate>
ODK.NO_CONFIGURATION_SET
</p>
</div>
</div>
</div>
</div>
</div>
116 changes: 116 additions & 0 deletions client/src/modules/odk-settings/odk-settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
angular.module('bhima.controllers')
.controller('ODKSettingsController', ODKSettingsController);

ODKSettingsController.$inject = [
'ODKSettingsService', 'util', 'NotifyService', 'SessionService', '$state', '$q',
];

/**
* ODK Settings Controller
*
* Provides configuration parameters for the link to ODK.
*/
function ODKSettingsController(
ODKSettings, util, Notify, Session, $state, $q,
) {
const vm = this;

vm.enterprise = Session.enterprise;
vm.settings = { };

vm.loading = false;

function refreshSettings() {
const settingsPromise = ODKSettings.read()
.then(settings => {
vm.hasODKConfiguration = settings.length > 0;
if (vm.hasODKConfiguration) {
[vm.settings] = settings;
}
});

const projectPromise = ODKSettings.getProjectSettings()
.then(project => {
vm.project = project;
});

const appUsersPromise = ODKSettings.getAppUsers()
.then(appUsers => {
vm.appUsers = appUsers;
});

vm.loading = true;
$q.all([settingsPromise, projectPromise, appUsersPromise])
.catch(Notify.handleError)
.finally(() => { vm.loading = false; });
}

// bind methods
vm.submit = submit;
vm.syncEnterprise = () => {
vm.loading = true;
ODKSettings.syncEnterprise()
.then(() => Notify.success('FORM.INFO.UPDATE_SUCCESS'))
.then(() => refreshSettings())
.catch(Notify.handleError)
.finally(() => { vm.loading = false; });
};

vm.syncAppUsers = () => {
vm.loading = true;

ODKSettings.syncAppUsers()
.then(() => Notify.success('FORM.INFO.UPDATE_SUCCESS'))
.then(() => refreshSettings())
.catch(Notify.handleError)
.finally(() => { vm.loading = false; });
};

vm.syncForms = () => {
vm.loading = true;
ODKSettings.syncForms()
.then(() => Notify.success('FORM.INFO.UPDATE_SUCCESS'))
.then(() => refreshSettings())
.catch(Notify.handleError)
.finally(() => { vm.loading = false; });
};

vm.syncSubmissions = () => {
vm.loading = true;
ODKSettings.syncSubmissions()
.then(() => Notify.success('FORM.INFO.UPDATE_SUCCESS'))
.then(() => refreshSettings())
.catch(Notify.handleError)
.finally(() => { vm.loading = false; });
};

// fired on startup
function startup() {
refreshSettings();
}

// form submission
function submit(form) {
vm.loading = true;
if (form.$invalid) {
Notify.danger('FORM.ERRORS.HAS_ERRORS');
return 0;
}

// make sure only fresh data is sent to the server.
if (form.$pristine) {
Notify.warn('FORM.WARNINGS.NO_CHANGES');
return 0;
}

const changes = angular.copy(vm.settings);

return ODKSettings.create(changes)
.then(() => Notify.success('FORM.INFO.UPDATE_SUCCESS'))
.then(() => refreshSettings())
.catch(Notify.handleError)
.finally(() => { vm.loading = false; });
}

startup();
}
Loading

0 comments on commit 37e0c07

Please sign in to comment.