Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Update ampathformstranslations.md #275

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build-test-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ jobs:
"username": "${{ secrets.NEXUS_USERNAME }}",
"password": "${{ secrets.NEXUS_PASSWORD }}"
}]
if: ${{ github.ref == 'refs/heads/master' && github.repository_owner == 'mekomsolutions' }}
if: ${{ github.ref == 'refs/heads/main' && github.repository_owner == 'mekomsolutions' }}

- name: Publish
run: mvn --batch-mode clean deploy -P validator
if: ${{ github.ref == 'refs/heads/master' && github.repository_owner == 'mekomsolutions' }}
if: ${{ github.ref == 'refs/heads/main' && github.repository_owner == 'mekomsolutions' }}
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ See the [documentation on Initializer's logging properties](readme/rtprops.md#lo

## Releases notes

#### Version 2.8.0
* Ampath forms translation files will now generate checksums.
* Enhancement to ensure that when an Ampath forms file is loaded, a new resource with the existing Ampath forms translations is created.

#### Version 2.7.0
* Added support for 'queues' domain.
* Added support for 'addresshierarchy' domain.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.openmrs.api.FormService;
import org.openmrs.api.db.ClobDatatypeStorage;
import org.openmrs.module.initializer.Domain;
import org.openmrs.module.initializer.api.ConfigDirUtil;
import org.openmrs.module.initializer.api.utils.Utils;
import org.openmrs.util.OpenmrsUtil;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -26,6 +27,8 @@ public class AmpathFormsLoader extends BaseFileLoader {

public static final String AMPATH_FORMS_UUID = "794c4598-ab82-47ca-8d18-483a8abe6f4f";

public static final String JSON_EXTENSION = "json";

@Autowired
private DatatypeService datatypeService;

Expand Down Expand Up @@ -83,6 +86,21 @@ protected void load(File file) throws Exception {
throw new Exception("Form Version is required");
}

// Delete Checksum Files for the translation files associated with the form
ConfigDirUtil configDirUtil = new ConfigDirUtil(iniz.getConfigDirPath(), iniz.getChecksumsDirPath(),
Domain.AMPATH_FORMS_TRANSLATIONS.getName(), true);

for (File translationFile : configDirUtil.getFiles(JSON_EXTENSION)) {
String js = FileUtils.readFileToString(translationFile, StandardCharsets.UTF_8.toString());
Map<String, Object> jf = new ObjectMapper().readValue(js, Map.class);
String translationForm = (String) jf.get("form");

if (StringUtils.equals(translationForm, formName)) {
configDirUtil
.deleteChecksumFile(replaceExtension(translationFile.getName(), ConfigDirUtil.CHECKSUM_FILE_EXT));
}
}

String uuid = Utils.generateUuidFromObjects(AMPATH_FORMS_UUID, formName, formVersion);
// Process Form
// ISSUE-150 If form with uuid present then update it
Expand Down Expand Up @@ -169,4 +187,22 @@ private void createNewForm(String uuid, String formName, String formDescription,
clobData.setValue(jsonString);
datatypeService.saveClobDatatypeStorage(clobData);
}

private static String replaceExtension(String fileName, String newExtension) {
// Validate inputs
if (StringUtils.isEmpty(fileName) || StringUtils.isEmpty(newExtension)) {
throw new IllegalArgumentException("File name and extension must not be null or empty");
}

// Find the last dot in the file name
int lastDotIndex = fileName.lastIndexOf('.');

// Handle the case where there's no dot in the file name
if (lastDotIndex == -1) {
return fileName + "." + newExtension;
}

// Replace the extension
return fileName.substring(0, lastDotIndex) + "." + newExtension;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,28 @@

import java.io.File;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.LocaleUtils;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.openmrs.Form;
import org.openmrs.FormResource;
import org.openmrs.api.FormService;
import org.openmrs.api.context.Context;
import org.openmrs.messagesource.PresentationMessage;
import org.openmrs.module.initializer.Domain;
import org.openmrs.module.initializer.InitializerMessageSource;
import org.openmrs.module.initializer.api.ConfigDirUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;

@Component
public class AmpathFormsTranslationsLoader extends BaseFileLoader {

private static final String LONG_FREE_TEXT_DATATYPE = "org.openmrs.customdatatype.datatype.LongFreeTextDatatype";

@Autowired
private FormService formService;

Expand All @@ -44,40 +46,42 @@ protected void load(File file) throws Exception {
String jsonTranslationsString = FileUtils.readFileToString(file, StandardCharsets.UTF_8.toString());
Map<String, Object> jsonTranslationsDefinition = new ObjectMapper().readValue(jsonTranslationsString, Map.class);

String formResourceUuid = (String) jsonTranslationsDefinition.get("uuid");
if (StringUtils.isBlank(formResourceUuid)) {
throw new IllegalArgumentException("Uuid is required for AMPATH forms translations loader.");
}

String language = (String) jsonTranslationsDefinition.get("language");
if (StringUtils.isBlank(language)) {
throw new IllegalArgumentException("'language' property is required for AMPATH forms translations loader.");
}

Form form = null;
FormResource formResource = Context.getFormService().getFormResourceByUuid(formResourceUuid);
if (formResource == null) {
formResource = new FormResource();
formResource.setUuid(formResourceUuid);
} else {
form = formResource.getForm();
}

String formName = (String) jsonTranslationsDefinition.get("form");
if (formName == null) {
throw new IllegalArgumentException("'form' property is required for AMPATH forms translations loader.");
}
form = formService.getForm(formName);
if (form == null) {
String formName = (String) jsonTranslationsDefinition.get("form");
if (formName == null) {
throw new IllegalArgumentException("'form' property is required for AMPATH forms translations loader.");
}
form = formService.getForm(formName);
if (form == null) {
throw new IllegalArgumentException(
"Could not find a form named '" + formName + "'. Please ensure an existing form is configured.");
throw new IllegalArgumentException(
"Could not find a form named '" + formName + "'. Please ensure an existing form is configured.");
}

FormResource formResource = null;
for (FormResource fr : formService.getFormResourcesForForm(form)) {
if (LONG_FREE_TEXT_DATATYPE.equals(fr.getDatatypeClassname())) {
Map<String, Object> jsonMap = new ObjectMapper().readValue(fr.getValue().toString(), Map.class);
if (jsonMap.containsKey("translations") && StringUtils.equals(jsonMap.get("language").toString(),
jsonTranslationsDefinition.get("language").toString())) {
formResource = fr;
break;
}
}
}

formResource.setForm(form);
formResource.setName(form.getName() + "_translations_" + language);
formResource.setDatatypeClassname("org.openmrs.customdatatype.datatype.LongFreeTextDatatype");
if (formResource == null) {
formResource = new FormResource();
formResource.setForm(form);
formResource.setName(form.getName() + "_translations_" + language);
formResource.setDatatypeClassname(LONG_FREE_TEXT_DATATYPE);
}

formResource.setValue(jsonTranslationsString);
formService.saveFormResource(formResource);

Expand All @@ -89,9 +93,4 @@ protected void load(File file) throws Exception {
LocaleUtils.toLocale(language), formNameTranslation, null));
}
}

@Override
public ConfigDirUtil getDirUtil() {
return new ConfigDirUtil(iniz.getConfigDirPath(), iniz.getChecksumsDirPath(), getDomainName(), true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.junit.runners.MethodSorters;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.openmrs.Form;
import org.openmrs.FormResource;
import org.openmrs.api.FormService;
import org.openmrs.api.context.Context;
Expand All @@ -31,8 +32,6 @@ public class AmpathFormsTranslationsLoaderIntegrationTest extends DomainBaseModu

private static final String FORM_TRANSLATIONS_FOLDER_PATH = "src/test/resources/ampathformstranslations/";

private static final String RESOURCE_UUID = "c5bf3efe-3798-4052-8dcb-09aacfcbabdc";

@Autowired
private AmpathFormsTranslationsLoader ampathFormsTranslationsLoader;

Expand Down Expand Up @@ -60,13 +59,12 @@ public void load_shouldLoadAFormTranslationsFileWithAllAttributesSpecifiedAsForm
ampathFormsTranslationsLoader.load();

// Verify
FormResource formResource = formService.getFormResourceByUuid(RESOURCE_UUID);
Form form = formService.getForm("Test Form 1");
FormResource formResource = formService.getFormResource(form, "Test Form 1_translations_fr");
Assert.assertNotNull(formResource);
Assert.assertEquals("c5bf3efe-3798-4052-8dcb-09aacfcbabdc", formResource.getUuid());

ObjectMapper mapper = new ObjectMapper();
JsonNode actualObj = mapper.readTree((String) formResource.getValue());
Assert.assertEquals("c5bf3efe-3798-4052-8dcb-09aacfcbabdc", actualObj.get("uuid").getTextValue());
Assert.assertEquals("French Translations", actualObj.get("description").getTextValue());
Assert.assertEquals("fr", actualObj.get("language").getTextValue());
Assert.assertEquals("Encontre", actualObj.get("translations").get("Encounter").getTextValue());
Expand All @@ -90,15 +88,14 @@ public void load_shouldLoadAndUpdateAFormTranslationsFileAsFormResource() throws
// Test that initial version loads in with expected values
ampathFormsTranslationsLoader.load();

FormResource formResource = formService.getFormResourceByUuid(RESOURCE_UUID);

// Verify
Form form = formService.getForm("Test Form 1");
FormResource formResource = formService.getFormResource(form, "Test Form 1_translations_fr");

Assert.assertNotNull(formResource);
Assert.assertEquals("c5bf3efe-3798-4052-8dcb-09aacfcbabdc", formResource.getUuid());

ObjectMapper mapper = new ObjectMapper();
JsonNode ampathTranslations = mapper.readTree((String) formResource.getValue());
Assert.assertEquals("c5bf3efe-3798-4052-8dcb-09aacfcbabdc", ampathTranslations.get("uuid").getTextValue());
Assert.assertEquals("French Translations", ampathTranslations.get("description").getTextValue());
Assert.assertEquals("fr", ampathTranslations.get("language").getTextValue());
Assert.assertEquals("Encontre", ampathTranslations.get("translations").get("Encounter").getTextValue());
Expand All @@ -115,13 +112,14 @@ public void load_shouldLoadAndUpdateAFormTranslationsFileAsFormResource() throws
// Replay
// Now load updated values
ampathFormsTranslationsLoader.load();
FormResource formResourceUpdated = formService.getFormResourceByUuid(RESOURCE_UUID);

Form formUpdated = formService.getForm("Test Form 1");
FormResource formResourceUpdated = formService.getFormResource(formUpdated, "Test Form 1_translations_fr");

// Verify
Assert.assertNotNull(formResourceUpdated);
ObjectMapper mapperUpdated = new ObjectMapper();
JsonNode ampathTranslationsUpdated = mapperUpdated.readTree((String) formResourceUpdated.getValue());
Assert.assertEquals("c5bf3efe-3798-4052-8dcb-09aacfcbabdc", ampathTranslationsUpdated.get("uuid").getTextValue());
Assert.assertEquals("French Translations Updated", ampathTranslationsUpdated.get("description").getTextValue());
Assert.assertEquals("fr", ampathTranslationsUpdated.get("language").getTextValue());
Assert.assertEquals("Tante", ampathTranslationsUpdated.get("translations").get("Aunt").getTextValue());
Expand All @@ -140,23 +138,6 @@ public void load_shouldThrowGivenInvalidFormAssociatedWithFormTranslations() thr

}

@Test
public void load_shouldThrowGivenMissingUuidPropertyInFormTranslationsDef() throws Exception {
// Setup
thrown.expectMessage("IllegalArgumentException: Uuid is required for AMPATH forms translations loader.");

String missingUuidTranslationDefFile = "src/test/resources/testdata/testAmpathformstranslations/invalid_form_missing_uuid_translations_fr.json";
File srcFile = new File(missingUuidTranslationDefFile);
File dstFile = new File(
ampathFormsTranslationsLoader.getDirUtil().getDomainDirPath() + "/test_form_translations_fr.json");

FileUtils.copyFile(srcFile, dstFile);

// Replay
ampathFormsTranslationsLoader.loadUnsafe(Collections.emptyList(), true);

}

@Test
public void load_shouldThrowGivenMissingFormFieldInFormTranslationsDef() throws Exception {
// Setup
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
<openmrsVersion2.1>2.1.1</openmrsVersion2.1>
<openmrsVersion2.2>2.2.0</openmrsVersion2.2>
<openmrsVersion2.3>2.3.6</openmrsVersion2.3>
<openmrsVersion2.4>2.4.0</openmrsVersion2.4>
<openmrsVersion2.4>2.4.5</openmrsVersion2.4>
<openmrsVersion2.5>2.5.5</openmrsVersion2.5>

<openmrsPlatformVersion>${openmrsVersion2.1}</openmrsPlatformVersion>
Expand Down
2 changes: 2 additions & 0 deletions readme/ampathforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,7 @@ The JSON source for these forms can be obtained from [AMPATH Form Builder](https

**NOTE:** Like other form engines (and as a result of the form tooling), the unique identifier for a form is its name. As a result, the `uuid` field provided by the Form Builder is usually `"xxxx"` and not used. Instead, the form UUID is determined based on the form name and the form version. Any previous version of the form with the same name, however, will also be replaced with whatever form is loaded by Initializer. It is therefore not recommended to combine Initializer with another mechanism for loading forms into the system, e.g., by using the Form Builder directly.

**NOTE:** When an Ampath forms file is loaded, a new resource with the existing Ampath forms translations is created

#### Further examples:
Please look at the test configuration folder for sample import files for all domains, see [here](../api/src/test/resources/testAppDataDir/configuration).
2 changes: 2 additions & 0 deletions readme/ampathformstranslations.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ ampathformstranslations/
└── form2_translations_en.json
```

**Note:** You will need a translation file for ALL languages you want to be able to switch between. For example, if you want to be able to switch between both French and English, you would need a file for BOTH those languages, as shown above. If you only had a fr.json file, then even when you set your language preference to English, you would still be seeing that form only in French.

Comment on lines +10 to +11
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will need a translation file for ALL languages you want to be able to switch between.

@gracepotma isn't this obvious tho? Or am I missing the point?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't obvious to me :P

###### JSON file example:
```json
{
Expand Down
Loading