diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 1981a11a..0b77b075 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,5 +4,5 @@ Fixes #[issue number] or Closes #[issue number] **Engineer Checklist** - [ ] I have run `./gradlew spotlessApply` to check my code follows the project's style guide -- [ ] I have built and run the efsity jar to verify my change fixes the issue and/or does not break the application +- [ ] I have built using the command `./gradlew clean assemble` and run the efsity jar to verify my change fixes the issue and does not break the application diff --git a/README.md b/README.md index fa4e1fc1..a49cfca9 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ A repo to hold our FHIR content and configuration creation tools and scripts. - [cleaner](https://github.com/onaio/fhircore-tooling/tree/main/cleaner) - [efsity](https://github.com/onaio/fhircore-tooling/tree/main/efsity) +- [efsity-ide](https://github.com/onaio/fhircore-tooling/tree/main/efsity-ide) - [importer](https://github.com/onaio/fhircore-tooling/tree/main/importer) - [sm-gen](https://github.com/onaio/fhircore-tooling/tree/main/sm-gen) - [uploader](https://github.com/onaio/fhircore-tooling/tree/main/uploader) diff --git a/efsity/build.gradle.kts b/efsity/build.gradle.kts index 13ab3768..b6793cd7 100644 --- a/efsity/build.gradle.kts +++ b/efsity/build.gradle.kts @@ -18,7 +18,7 @@ repositories { group = "org.smartregister" -version = "2.3.8-SNAPSHOT" +version = "2.3.9-SNAPSHOT" description = "fhircore-tooling (efsity)" @@ -83,7 +83,8 @@ dependencies { implementation(deps.picocli) implementation(deps.xstream) implementation(deps.icu4j) - implementation(deps.javafaker) + implementation(deps.javafaker) { exclude(group = "org.yaml") } + implementation(deps.snakeyaml) testImplementation(kotlin("test")) testImplementation("junit:junit:4.13.2") diff --git a/efsity/libs.versions.toml b/efsity/libs.versions.toml index 9dd17858..c437a9e4 100644 --- a/efsity/libs.versions.toml +++ b/efsity/libs.versions.toml @@ -2,7 +2,7 @@ buildConfig-version="4.1.2" caffeine-version="2.9.3" common-compress-version="1.22" -cql-version="2.11.0" +cql-version="3.3.2" gson-version="2.10.1" hapi-fhir-core-version="5.6.106" hapi-fhir-utilities-version="3.8.0" @@ -15,6 +15,7 @@ jsonschemafriend-version="0.12.2" kotlin-stdlib="1.8.22" opencds-cql-version="2.4.0" project-build-sourceEncoding="UTF-8" +snakeyaml-version="2.3" spotless-version ="6.20.0" xstream="1.4.20" icu4j-version = "75.1" @@ -46,6 +47,7 @@ jackson-core = { module = "com.fasterxml.jackson.core:jackson-core", version.ref jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson-version" } javafaker = { module = "com.github.javafaker:javafaker", version.ref = "javafaker-version"} picocli = { module = "info.picocli:picocli", version.ref = "info-picocli-version" } +snakeyaml = { module="org.yaml:snakeyaml", version.ref ="snakeyaml-version"} xstream = { module = "com.thoughtworks.xstream:xstream", version.ref = "xstream" } icu4j = { module="com.ibm.icu:icu4j", version.ref = "icu4j-version" } diff --git a/efsity/src/main/java/org/smartregister/command/QuestionnaireResponseGeneratorCommand.java b/efsity/src/main/java/org/smartregister/command/QuestionnaireResponseGeneratorCommand.java index dad7ebc6..3eddb2ee 100644 --- a/efsity/src/main/java/org/smartregister/command/QuestionnaireResponseGeneratorCommand.java +++ b/efsity/src/main/java/org/smartregister/command/QuestionnaireResponseGeneratorCommand.java @@ -174,7 +174,7 @@ public static LocalDate generateRandomDate() { LocalDate startDate = LocalDate.of(1960, 1, 1); LocalDate endDate = LocalDate.of(2023, 12, 31); long daysBetween = ChronoUnit.DAYS.between(startDate, endDate); - long randomDays = random.nextLong(daysBetween + 1); + long randomDays = (long) getRandomNumber(daysBetween + 1); return startDate.plusDays(randomDays); } @@ -182,7 +182,7 @@ public static LocalDateTime generateRandomDateTime() { LocalDateTime startDateTime = LocalDateTime.of(2000, 1, 1, 0, 0); LocalDateTime endDateTime = LocalDateTime.of(2024, 12, 31, 23, 59); long secondsBetween = ChronoUnit.SECONDS.between(startDateTime, endDateTime); - long randomSeconds = random.nextLong(secondsBetween + 1); + long randomSeconds = (long) getRandomNumber(secondsBetween + 1); return startDateTime.plusSeconds(randomSeconds); } @@ -193,7 +193,7 @@ public static JSONObject generateChoiceValue(JSONArray questions, String link_id if (Objects.equals(current_id, link_id)) { if (current_object.has("answerOption")) { JSONArray answer_options = current_object.getJSONArray("answerOption"); - int random_index = random.nextInt(answer_options.length()); + int random_index = (int) getRandomNumber(answer_options.length() + 1); return answer_options.getJSONObject(random_index).getJSONObject("valueCoding"); } } @@ -226,7 +226,7 @@ public static JSONObject generateQuantityValue(JSONArray questions, String link_ } } JSONObject quantityAnswer = new JSONObject(); - quantityAnswer.put("value", random.nextInt(minValue, maxValue)); + quantityAnswer.put("value", getRandomNumber(minValue, maxValue)); quantityAnswer.put("unit", unit); quantityAnswer.put("system", "http://unitsofmeasure.org"); quantityAnswer.put("code", unit); @@ -236,7 +236,7 @@ public static JSONObject generateQuantityValue(JSONArray questions, String link_ public static JSONObject generateReferenceValue() { List exampleResourceTypes = Arrays.asList("Patient", "Practitioner", "Location", "Immunization"); - int randomPick = random.nextInt(0, exampleResourceTypes.size()); + int randomPick = (int) getRandomNumber(0, exampleResourceTypes.size()); JSONObject reference = new JSONObject(); reference.put( "reference", @@ -269,10 +269,11 @@ static JSONObject generateAnswer( switch (type.toLowerCase()) { case "string": return answer.put( - "valueString", result != null ? result.toString() : "FakeString" + random.nextInt(100)); + "valueString", + result != null ? result.toString() : "FakeString" + getRandomNumber(100)); case "integer": return answer.put( - "valueInteger", result instanceof Integer ? (Integer) result : random.nextInt(100)); + "valueInteger", result instanceof Integer ? (Integer) result : getRandomNumber(100)); case "boolean": return answer.put( "valueBoolean", result instanceof Boolean ? (Boolean) result : random.nextBoolean()); @@ -296,7 +297,7 @@ static JSONObject generateAnswer( case "text": return answer.put( "valueString", - result != null ? result.toString() : "This is a fake text" + random.nextInt(100)); + result != null ? result.toString() : "This is a fake text" + getRandomNumber(100)); case "reference": return answer.put("valueReference", generateReferenceValue()); default: @@ -439,6 +440,21 @@ private static String aiGenerated( } } + public static double getRandomNumber(T bound) { + return random.nextDouble() * bound.doubleValue(); + } + + public static double getRandomNumber(T origin, T bound) { + double originValue = origin.doubleValue(); + double boundValue = bound.doubleValue(); + + if (originValue >= boundValue) { + throw new IllegalArgumentException("Origin must be less than bound"); + } + + return originValue + random.nextDouble() * (boundValue - originValue); + } + public static class Constants { public static final String QUESTIONNAIRE_RESPONSE_SUFFIX = "-response.json"; } diff --git a/efsity/src/main/kotlin/org/smartregister/external/CqlToLibraryConvertServices.kt b/efsity/src/main/kotlin/org/smartregister/external/CqlToLibraryConvertServices.kt index 160752d1..9ae54197 100644 --- a/efsity/src/main/kotlin/org/smartregister/external/CqlToLibraryConvertServices.kt +++ b/efsity/src/main/kotlin/org/smartregister/external/CqlToLibraryConvertServices.kt @@ -3,11 +3,9 @@ package org.smartregister.external import com.ibm.icu.impl.Assert.fail import org.cqframework.cql.cql2elm.CqlTranslator -import org.cqframework.cql.cql2elm.CqlTranslatorOptions import org.cqframework.cql.cql2elm.LibraryManager import org.cqframework.cql.cql2elm.ModelManager import org.cqframework.cql.cql2elm.quick.FhirLibrarySourceProvider -import org.fhir.ucum.UcumEssenceService import org.hl7.fhir.r4.model.Attachment import org.hl7.fhir.r4.model.Enumerations import org.hl7.fhir.r4.model.Library @@ -49,21 +47,14 @@ class CqlToLibraryConvertServices { * @param cqlText the CQL Library * @return a [CqlTranslator] object that contains the elm representation of the library inside it. */ - private fun compile(cqlText: String): CqlTranslator { + fun compile(cqlText: String): CqlTranslator { val modelManager = ModelManager() val libraryManager = LibraryManager(modelManager).apply { librarySourceLoader.registerProvider(FhirLibrarySourceProvider()) } - val translator = - CqlTranslator.fromText( - cqlText, - modelManager, - libraryManager, - UcumEssenceService(this::class.java.getResourceAsStream("/ucum-essence.xml")), - *CqlTranslatorOptions.defaultOptions().options.toTypedArray() - ) + val translator = CqlTranslator.fromText(cqlText, libraryManager) // Helper makes sure the test CQL compiles. Reports an error if it doesn't if (this.isStrictMode && translator.errors.isNotEmpty()) { @@ -84,21 +75,20 @@ class CqlToLibraryConvertServices { } /** - * Assembles an ELM Library exported as a JSON in to a FHIRLibrary + * Assembles an ELM Library exported as a JSON into a FHIRLibrary * * @param jsonElmStr the JSON representation of the ELM Library * @param libName the Library name * @param libVersion the Library Version * @return a FHIR Library that includes the ELM Library. */ - private fun assembleFhirLib( + fun assembleFhirLib( cqlStr: String?, jsonElmStr: String?, xmlElmStr: String?, libName: String, - libVersion: String + libVersion: String, ): Library { - val attachmentCql = cqlStr?.let { Attachment().apply { @@ -129,7 +119,7 @@ class CqlToLibraryConvertServices { version = libVersion status = Enumerations.PublicationStatus.ACTIVE experimental = true - url = "http://localhost/Library/$libName|$libVersion" + url = "http://localhost/Library/$libName" attachmentCql?.let { addContent(it) } attachmentJson?.let { addContent(it) } attachmentXml?.let { addContent(it) } diff --git a/efsity/src/main/kotlin/org/smartregister/external/FhirCarePlanGeneratorLite.kt b/efsity/src/main/kotlin/org/smartregister/external/FhirCarePlanGeneratorLite.kt index a78c5795..cf489d64 100644 --- a/efsity/src/main/kotlin/org/smartregister/external/FhirCarePlanGeneratorLite.kt +++ b/efsity/src/main/kotlin/org/smartregister/external/FhirCarePlanGeneratorLite.kt @@ -75,7 +75,7 @@ class FhirCarePlanGeneratorLite { ) ) - if (planDefinition == null || planDefinition.action == null || planDefinition.action.size < 1) { + if (planDefinition.action == null || planDefinition.action.size < 1) { FctUtils.printWarning("No Actions defined found for the Plan definition") } else FctUtils.printInfo( diff --git a/efsity/src/main/kotlin/org/smartregister/external/TransformSupportServices.kt b/efsity/src/main/kotlin/org/smartregister/external/TransformSupportServices.kt index bec23f46..f7cb7642 100644 --- a/efsity/src/main/kotlin/org/smartregister/external/TransformSupportServices.kt +++ b/efsity/src/main/kotlin/org/smartregister/external/TransformSupportServices.kt @@ -1,6 +1,8 @@ /* (C)2021-2023 */ package org.smartregister.external +import javax.inject.Inject +import javax.inject.Singleton import org.hl7.fhir.exceptions.FHIRException import org.hl7.fhir.r4.context.SimpleWorkerContext import org.hl7.fhir.r4.model.AdverseEvent @@ -22,18 +24,25 @@ import org.hl7.fhir.r4.model.Task import org.hl7.fhir.r4.model.Timing import org.hl7.fhir.r4.terminologies.ConceptMapEngine import org.hl7.fhir.r4.utils.StructureMapUtilities.ITransformerServices +import org.smartregister.util.FctUtils /** - * 3RD PARTY CODE: Copied from - * https://github.com/opensrp/fhircore/blob/main/android/engine/src/main/java/org/smartregister/fhircore/engine/util/helper/TransformSupportServices.kt + * Copied from + * https://github.com/hapifhir/org.hl7.fhir.core/blob/master/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/TransformSupportServices.java + * and adapted for R4. This class enables us to implement generation of Types and Resources not in + * the original Hapi Fhir source code here + * https://github.com/hapifhir/org.hl7.fhir.core/blob/master/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ResourceFactory.java. + * The missing Types and Resources are internal model types eg RiskAssessment.Prediction, + * Immunization.Reaction */ -class TransformSupportServices constructor(private val simpleWorkerContext: SimpleWorkerContext) : +@Singleton +class TransformSupportServices @Inject constructor(val simpleWorkerContext: SimpleWorkerContext) : ITransformerServices { - private val outputs: MutableList = mutableListOf() + val outputs: MutableList = mutableListOf() override fun log(message: String) { - // logger.info(message) + FctUtils.printInfo(message) } @Throws(FHIRException::class)