Skip to content

Commit

Permalink
Merge pull request #114 from onaio/questionnair-response-item-id-vali…
Browse files Browse the repository at this point in the history
…dation

validates the questionnaire response item id and resource name
  • Loading branch information
brandyodhiambo authored Jan 31, 2024
2 parents a081e65 + bf30414 commit a30608e
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 133 deletions.
188 changes: 94 additions & 94 deletions sm-gen/src/main/kotlin/org.smartregister.fhir.structuremaptool/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ca.uhn.fhir.parser.IParser
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.prompt
import org.apache.commons.codec.Resources
import com.google.gson.GsonBuilder
import org.apache.commons.io.FileUtils
import org.apache.poi.ss.usermodel.CellType
Expand All @@ -22,6 +23,7 @@ import org.hl7.fhir.utilities.npm.ToolsVersion
import java.io.File
import java.io.FileInputStream
import java.nio.charset.Charset
import java.util.*

fun main(args: Array<String>) {
Application().main(args)
Expand All @@ -48,31 +50,10 @@ REMAINING TASKS
*/

class Application : CliktCommand() {
val xlsFileName: String by option(help = "XLS filepath").prompt("Kindly enter the XLS filename")
// val xlsfile: String by option(help = "XLS filepath").prompt("Kindly enter the XLS filepath")
val questionnaireFileName: String by option(help = "Questionnaire filename").prompt("Kindly enter the questionnaire filename")
//val questionnairefile : String by option(help = "Questionnaire filepath").prompt("Kindly enter the questionnaire filepath")
val xlsfile: String by option(help = "XLS filepath").prompt("Kindly enter the XLS filepath")
val questionnairefile : String by option(help = "Questionnaire filepath").prompt("Kindly enter the questionnaire filepath")

override fun run() {
var xlsfile= ""
var questionnairefile = ""

val xlsUrl = Application::class.java.getResource("/$xlsFileName")
val questionnaireUrl = Application::class.java.getResource("/$questionnaireFileName")

// Check if the resource exists
if (xlsUrl != null && questionnaireUrl != null) {
// Xls file path extraction
val xlsfilePath = File(xlsUrl.toURI())
xlsfile = xlsfilePath.absolutePath

// questionnaire file path extraction
val questionnaireFilePath = File(questionnaireUrl.toURI())
questionnairefile = questionnaireFilePath.absolutePath
} else {
println("Resource not found: $xlsFileName")
println("Resource not found: $questionnaireFileName")
}

/*
Expand Down Expand Up @@ -121,6 +102,7 @@ class Application : CliktCommand() {
val xlsFile = FileInputStream(xlsfile)
val xlWb = WorkbookFactory.create(xlsFile)


// TODO: Check that all the Resource(s) ub the Resource column are the correct name and type eg. RiskFlag in the previous XLSX was not valid
// TODO: Check that all the path's and other entries in the excel sheet are valid
// TODO: Add instructions for adding embedded classes like `RiskAssessment$RiskAssessmentPredictionComponent` to the TransformSupportServices
Expand Down Expand Up @@ -151,110 +133,128 @@ class Application : CliktCommand() {
TODO: Fix Groups calling sequence so that Groups that depend on other resources to be generated need to be called first
We can also throw an exception if to figure out cyclic dependency. Good candidate for Floyd's tortoise and/or topological sorting 😁. Cool!!!!
*/
val questionnaireResponseItemIds = questionnaireResponse.item.map { it.id }
if(questionnaireId != null && questionnaireResponseItemIds.isNotEmpty()){

val sb = StringBuilder()
val structureMapHeader = """
map "http://hl7.org/fhir/StructureMap/$questionnaireId" = '${questionnaireId?.clean()}'
val sb = StringBuilder()
val structureMapHeader = """
map "http://hl7.org/fhir/StructureMap/$questionnaireId" = '${questionnaireId.clean()}'
uses "http://hl7.org/fhir/StructureDefinition/QuestionnaireReponse" as source
uses "http://hl7.org/fhir/StructureDefinition/Bundle" as target
""".trimIndent()

val structureMapBody = """
group ${questionnaireId?.clean()}(source src : QuestionnaireResponse, target bundle: Bundle) {
val structureMapBody = """
group ${questionnaireId.clean()}(source src : QuestionnaireResponse, target bundle: Bundle) {
src -> bundle.id = uuid() "rule_c";
src -> bundle.type = 'collection' "rule_b";
src -> bundle.entry as entry then """.trimIndent()

/*
/*
Create a mapping of COLUMN_NAMES to COLUMN indexes
Create a mapping of COLUMN_NAMES to COLUMN indexes
*/
//val mapColumns
*/
//val mapColumns


val lineNos = 1
var firstResource = true
val extractionResources = hashMapOf<String, Resource>()
val resourceConversionInstructions = hashMapOf<String, MutableList<Instruction>>()
val lineNos = 1
var firstResource = true
val extractionResources = hashMapOf<String, Resource>()
val resourceConversionInstructions = hashMapOf<String, MutableList<Instruction>>()

// Group the rules according to the resource
val fieldMappingsSheet = xlWb.getSheet("Field Mappings")
fieldMappingsSheet.forEachIndexed { index, row ->
if (index == 0) return@forEachIndexed
// Group the rules according to the resource
val fieldMappingsSheet = xlWb.getSheet("Field Mappings")
fieldMappingsSheet.forEachIndexed { index, row ->
if (index == 0) return@forEachIndexed

if (row.isEmpty()) {
return@forEachIndexed
}
if (row.isEmpty()) {
return@forEachIndexed
}

val instruction = row.getInstruction()
if (instruction.resource.isNotEmpty()) {
resourceConversionInstructions.computeIfAbsent(instruction.searchKey(), { key -> mutableListOf() })
.add(instruction)
}
}
//val resource = ?: Class.forName("org.hl7.fhir.r4.model.$resourceName").newInstance() as Resource

val instruction = row.getInstruction()
val xlsId = instruction.responseFieldId
val comparedResponseAndXlsId = questionnaireResponseItemIds.contains(xlsId)
if (instruction.resource.isNotEmpty() && comparedResponseAndXlsId) {
resourceConversionInstructions.computeIfAbsent(instruction.searchKey(), { key -> mutableListOf() })
.add(instruction)
}
}
//val resource = ?: Class.forName("org.hl7.fhir.r4.model.$resourceName").newInstance() as Resource

// Perform the extraction for the row
/*generateStructureMapLine(structureMapBody, row, resource, extractionResources)

extractionResources[resourceName + resourceIndex] = resource*/
// Perform the extraction for the row
/*generateStructureMapLine(structureMapBody, row, resource, extractionResources)
sb.append(structureMapHeader)
sb.appendNewLine().appendNewLine().appendNewLine()
sb.append(structureMapBody)
extractionResources[resourceName + resourceIndex] = resource*/

// Fix the questions path
val questionsPath = getQuestionsPath(questionnaire)
sb.append(structureMapHeader)
sb.appendNewLine().appendNewLine().appendNewLine()
sb.append(structureMapBody)

// TODO: Generate the links to the group names here
var index = 0
var len = resourceConversionInstructions.size
resourceConversionInstructions.forEach { entry ->
val resourceName = entry.key.capitalize()
if (index++ != 0) sb.append(", ")
sb.append("Extract$resourceName(src, bundle)")
}
sb.append(""" "rule_a";""".trimMargin())
sb.appendNewLine()
sb.append("}")
// Fix the questions path
val questionsPath = getQuestionsPath(questionnaire)

// Add the embedded instructions
val groupNames = mutableListOf<String>()
// TODO: Generate the links to the group names here
var index = 0
var len = resourceConversionInstructions.size
var resourceName = ""
resourceConversionInstructions.forEach { entry ->
resourceName = entry.key.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
if (index++ != 0) sb.append(",")
if(resourceName.isNotEmpty()) sb.append("Extract$resourceName(src, bundle)")
}
sb.append(""" "rule_a";""".trimMargin())
sb.appendNewLine()
sb.append("}")

sb.appendNewLine().appendNewLine().appendNewLine()
// Add the embedded instructions
val groupNames = mutableListOf<String>()

resourceConversionInstructions.forEach {
Group(it, sb, questionsPath)
.generateGroup(questionnaireResponse)
}
sb.appendNewLine().appendNewLine().appendNewLine()

val structureMapString = sb.toString()
try {
val simpleWorkerContext = SimpleWorkerContext().apply {
setExpansionProfile(Parameters())
isCanRunWithoutTerminology = true
resourceConversionInstructions.forEach {
Group(it, sb, questionsPath)
.generateGroup(questionnaireResponse)
}
val transformSupportServices = TransformSupportServices(simpleWorkerContext)
val scu = org.hl7.fhir.r4.utils.StructureMapUtilities(simpleWorkerContext, transformSupportServices)
val structureMap = scu.parse(structureMapString, questionnaireId!!.clean())
// DataFormatException | FHIRLexerException

val bundle = Bundle()

scu.transform(contextR4, questionnaireResponse, structureMap, bundle)
val structureMapString = sb.toString()
try {
val simpleWorkerContext = SimpleWorkerContext().apply {
setExpansionProfile(Parameters())
isCanRunWithoutTerminology = true
}
val transformSupportServices = TransformSupportServices(simpleWorkerContext)
val scu = org.hl7.fhir.r4.utils.StructureMapUtilities(simpleWorkerContext, transformSupportServices)
val structureMap = scu.parse(structureMapString, questionnaireId.clean())
// DataFormatException | FHIRLexerException

try{
val bundle = Bundle()
scu.transform(contextR4, questionnaireResponse, structureMap, bundle)
val jsonParser = FhirContext.forR4().newJsonParser()

println(jsonParser.encodeResourceToString(bundle))
} catch (e:Exception){
e.printStackTrace()
}

} catch (ex: Exception) {
println("The generated StructureMap has a formatting error")
ex.printStackTrace()
}

val jsonParser = FhirContext.forR4().newJsonParser()
var finalStructureMap = sb.toString()
finalStructureMap = finalStructureMap.addIdentation()
println(finalStructureMap)

println(jsonParser.encodeResourceToString(bundle))
} catch (ex: Exception) {
System.out.println("The generated StructureMap has a formatting error")
ex.printStackTrace()
// TODO: Generate JSON version
// TODO: Provide both as new files
writeStructureMapOutput(sb.toString().addIdentation())
}
writeStructureMapOutput(sb.toString().addIdentation())

}

fun Row.getInstruction() : Instruction {
Expand Down
Loading

0 comments on commit a30608e

Please sign in to comment.