Hello @Michael Wood,

The line property is actually a list of string in address object property. So You should use direct Insert action instead of Set for that property when doing mapping in DTL for single values. If it's list/collection the you should foreach the list property and insert the values to the line() property. Refer the screenshots below for both scenarios.

Direct single Insert

XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
{
<transform sourceClass='CSVtoHL7.Inputfile.Record' targetClass='HS.FHIR.DTL.vR4.Model.Resource.Location' create='new' language='objectscript' >
<assign value='source.FirstName' property='target.address.line' action='insert' key='1' />
</transform>
}

List 

XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
{
<transform sourceClass='CSVtoHL7.Inputfile.Record' targetClass='HS.FHIR.DTL.vR4.Model.Resource.Location' create='new' language='objectscript' >
<foreach property='source.AddressLine()' key='k1' >
<assign value='source.AddressLine.(k1)' property='target.address.line' action='insert' key='k1' />
</foreach>
</transform>
}

result

No. GetColumnType() method returns an integer of the datatype.You can manually get the datatype from the integer.

Class Sample.Person Extends %Persistent
{

Property FirstName As %String;

Property LastName As %String;

Property StartDate As %Library.TimeStamp ;

Property Age As %Integer;

Property TestCurrency As %Currency;

Property TestBoolean As %Boolean;

Property TestCharStream As %Stream.GlobalCharacter;

Query TestQuery() As %SQLQuery [ SqlProc ]
{
    
    select firstname,LastName,Age,TestCurrency,TestBoolean,TestCharStream from Sample.Person
}

ClassMethod GetDataTypeOfField() As %String
{
    set result = ##class(%ResultSet).%New("Sample.Person:TestQuery")
    ;	
    for I=1:1:result.GetColumnCount() {
        write "Column Name: ",result.GetColumnName(I),"  "
        write "Datatype number: ",result.GetColumnType(I),"  "
        write "DataType: ",..GetDataType(result.GetColumnType(I)),!!
    }
}
///Get datatype from the integer
ClassMethod GetDataType(type As %Integer=0) [ CodeMode = expression ]
{
$Case(type,
        1:"BINARY", 2:"DATE", 3:"DOUBLE", 4:"HANDLE",
        5:"INTEGER", 6:"LIST", 	7:"LONGVARCHAR",
        8:"TIME", 9:"TIMESTAMP", 10:"VARCHAR", 11:"STATUS",
        12:"BINARYSTREAM", 13:"CHARACTERSTREAM", 14:"NUMERIC",
        15:"CURRENCY", 16:"BOOLEAN", 17:"OID",
        18:"BIGINT", 19:"FDATE", 20:"FTIMESTAMP",
        :"")
}
}

The output when execute the method GetDataTypeOfField()

IRISMYDEV>d ##class(Sample.Person).GetDataTypeOfField()
Column Name: FirstName  Datatype number: 10  DataType: VARCHAR
 
Column Name: LastName  Datatype number: 10  DataType: VARCHAR
 
Column Name: Age  Datatype number: 5  DataType: INTEGER
 
Column Name: TestCurrency  Datatype number: 15  DataType: CURRENCY
 
Column Name: TestBoolean  Datatype number: 16  DataType: BOOLEAN
 
Column Name: TestCharStream  Datatype number: 13  DataType: CHARACTERSTREAM

refer the documentation here

Hello David,

Both object script and SQL transaction rollback's are captured in log once you enabled the setting is true  System > Configuration > Compatibility Settings > Edit Compatibility Settings  for rollback log

USER>write $ZV
IRIS for Windows (x86-64) 2023.1 (Build 229) Fri Apr 14 2023 17:36:18 EDT
USER>tstart
 
TL1:USER>set ^TEST=1
 
TL1:USER>trollback

log entry

 

This is nice. Anyway there are few suggestions

  1. Make sure the commands and string functions are following same format either SET or Set or set 
  2. Use %DynmaicArray instead of literal constructors  [ ]. Because it's instantiate the methods of the object like %push when accessing it. Literal constructors won't do it.
  3. Comments are essential for better understanding the program flow. Anyway if you don't want to appear your comments in INT code then use the syntax #;  instead of ;  ex: #;Test comment
  4. Use $ListNext to get the list values one by one. It rid out the 2 additional string functions $LISTLENGTH and $LISTGET usage in your case. Easy to understand 
  5. You can use $Increment instead of declaring a   questionNumber variable and do addition operation in the code.
  6. Add some conditional checks to skip the if the values in between | is empty

I have attached the code sample below. 


ClassMethod createResponse(data As %String(MAXLEN="")) As %DynamicArray
{
	
	set items = ##class(%DynamicArray).%New()
	
	#;1.- Questions splitted by "|"
	set ptr=0
	set listQuestions = $ListFromString(data, "|")
    
    #;2.- Iterate
    while $ListNext(listQuestions,ptr,questionAnswer) {
	   	#;3.- Update variables
	   	continue:questionAnswer=""
	    set question= $Piece(questionAnswer, ":", 1)
        set answer 	= $ZStrip($PIECE(questionAnswer, ":", 2), "<W") //Get rid of initial whitespace
        #;4.- Generate item
    	set item 	= 									
        	{
        	"definition": ("question "_($Increment(questionNumber))),
        	"text": (question),
        	"answer": 
			        [
			            {
			                "valueString": (answer)
			            }
			        ]
        	}
        do items.%Push(item)
    }	
    return items
}

Hope this helps  

AFAIK No. There is no straightforward way to import JSON from FHIR discrete resources to SDA3 objects by %JSONImport(). Basically, there are various stages involved in converting the FHIR to HL7 and vice versa. To achieve this, Intersystems created an intermediary format called SDA. However, there are more processes involved whenever convert the bundle or discrete resource.
for example

  1. In some instances, the FHIR resource data element name (property) is not the same as the SDA property.
  2. Lots of internal DTL's are invoked or invoked at the conversion time, and an SDA object is created based on that output. Typically it's common for SDA to FHIR and vice versa. 
  3. DTL's are crucial to accomplish this conversion. Some of the data elements are not mapped in the standard FHIR to SDA or SDA to FHIR DTL transformation. HS.FHIR.DTL.vR4.SDA3.AllergyIntolerance.Allergy in this DTL the "criticality" data element is not mapped with SDA object In this case you should create your custom DTL from the already implemented DTL to convert the values to object. So If you haven't added this type of additional properties in the SDA extension class, it won't work.
  4. Metadata values and lookup tables vary from SDA to FHIR. SDA has 'A' in the lookup tables for some fields, while FHIR has 'Active'. There is a internal validation runs against every data element to verify the generated FHIR resource Every time

I'm not sure why the request class CSVtoHL7.Inputfile.Record inherits from right. All the request and response are required persistent object. This will be used to display the entire flow in the visual trace section. I have attached some sample below.

You can add a property setter method for property DOB and modify the value from MM/DD/YYYY to +$H value. This will keep the internal date format in database.

Class CSVtoHL7.Inputfile.Record Extends (Ens.Request, %XML.Adaptor, EnsLib.RecordMap.Base) [ ProcedureBlock ]
{
Property ID As %Integer;
Property LastName As %String;
Property FirstName As %String;
Property MiddleName As %String;
Property DOB As %Date;
Method DOBSet(pDate) As %Status
{
    Set i%DOB= $ZDH(pDate)
    Quit $$$OK
}
Property Gender As %String;
ClassMethod createObj() As CSVtoHL7.Inputfile.Record
{
    Set obj = ##class(CSVtoHL7.Inputfile.Record).%New()
    Set obj.DOB="12/30/2001"
    Set obj.FirstName="Test"
    Set obj.ID=12345
    Set obj.MiddleName = "middle"
    Set obj.Gender="M"
    return obj
}
}

Create a object for the request class and send to Transformation. you can use the logic $translate($ZDT(source.DOB_",0",3)," ","T")_"Z" in DTL to convert the internal date format to required output 2023-08-24T00:00:00Z. You can refer the DTL sample below

Class CSVtoHL7.DTL.Record Extends Ens.DataTransformDTL [ DependsOn = (CSVtoHL7.Inputfile.Record, EnsLib.HL7.Message) ]
{

Parameter IGNOREMISSINGSOURCE = 1;

Parameter REPORTERRORS = 1;

Parameter TREATEMPTYREPEATINGFIELDASNULL = 0;

XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
{
<transform sourceClass='CSVtoHL7.Inputfile.Record' targetClass='EnsLib.HL7.Message' targetDocType='2.5:ADT_A01' create='new' language='objectscript' >
<assign value='source.ID' property='target.{PID:SetIDPID}' action='set' />
<assign value='source.FirstName' property='target.{PID:PatientName().FamilyName}' action='set' />
<assign value='source.MiddleName' property='target.{PID:PatientName().GivenName}' action='set' />
<assign value='source.Gender' property='target.{PID:AdministrativeSex}' action='set' />
<assign value='$translate($ZDT(source.DOB_",0",3)," ","T")_"Z"' property='target.{PID:DateTimeofBirth.Time}' action='set' />
</transform>
}

}

output

As we mentioned above the DOB should have +$H value instead of MM/DD/YYYY. However you can try the below

If DOB is date format

IRISMYDEV>set dob="12/01/1993"
IRISMYDEV>write $translate($ZDT($ZDTH(dob),3)," ","T")_"Z"
1993-12-01T00:00:00Z

If DOB is +$H value

IRISMYDEV>set dob=+$H
IRISMYDEV>write $translate($ZDT(d_",00000",3)," ","T")_"Z"
2023-08-24T00:00:00Z

Hello Smythe,

I agree with @Robert Cemper points. The %Date datatype is for +$H which means numeric date value. Not an string. You should modify the datatype of the property or use string functions.

IRISMYDEV>s obj = ##Class(CSVtoHL7.Inputfile.Record).%New()
IRISMYDEV>s obj.DOB="12/12/1993"
IRISMYDEV>zw ##Class(%Date).IsValid(obj.DOB)
"0 "_$lb($lb(7207,"12/12/1993",,,,,,,,$lb(,"IRISMYDEV",$lb("e^IsValid+1^%Library.Date.1^1","e^^^0"))))/* ERROR #7207: Datatype value '12/12/1993' is not a valid number */
 
IRISMYDEV>w $SYSTEM.OBJ.DisplayError()
ERROR #7207: Datatype value '12/12/1993' is not a valid number1
IRISMYDEV>s obj = ##Class(CSVtoHL7.Inputfile.Record).%New()
IRISMYDEV>s obj.DOB=$ZDateH("12/12/1993")
IRISMYDEV>zw ##Class(%Date).IsValid(obj.DOB)
1

Hello Pierre,

You have two options to get query parameters

  1. You can merge the entire %request.Data into local array and use string function.
  2. User %request.Next(data) method to loop the %request.Data one by one and get query parameter values

ClassMethod GetQueryParams()
{
	set data=""
	For {
		set data  = %request.Next(data) quit:data=""
		write data,!
	}
}
OUTPUT
Bottom
Name
Top

For cgiEnvs. You can follow same merge option to get all values. Otherwise use  %request.NextCgiEnv(cgi) to get the list of available values.

ClassMethod GetcgiEnvs()
{
    set cgi=""
    for {
        set cgi  = %request.NextCgiEnv(cgi) quit:cgi=""
        write cgi,!
    }
}

If you're receiving unexpected fields(key value pairs) as part of the JSON and the properties are not included in the class definition. You need to add the below parameter in your class definition(%JSON.Adaptor extended class). This will ignore loading the unexpected field.

Parameter %JSONIGNOREINVALIDFIELD As BOOLEAN = 1

In addition, The JSON key-value pair data type should match with class definition property datatype.