Eduard Lebedyuk · May 2, 2024 go to post

There are two formats for LUT:

Old one:

<?xml version="1.0" encoding="UTF-8"?>
<Export generator="IRIS" version="26" zv="IRIS for UNIX (Red Hat Enterprise Linux 7 for x86-64) 2022.1 (Build 209U)" ts="2024-03-03 06:05:36">
    <Document name="LUT_NAME.LUT">
        <lookupTable>
            <entry table="LUT_NAME" key="KEY">VALUE</entry>
            <entry table="LUT_NAME" key="KEY2">VALUE2</entry>
        </lookupTable>
    </Document>
</Export>

New one:

<?xml version="1.0"?>
<lookupTable>
    <entry table="LUT_NAME" key="KEY">VALUE</entry>
    <entry table="LUT_NAME" key="KEY2">NALUE2</entry>
</lookupTable>

Looks like you're importing old format using new importer. Here's the code to import both versions:

ClassMethod ImportLUT(dir)
{
	#include %occErrors
	write "Lookup Tables import from: " _ dir
	set rs = ##class(%File).FileSetFunc(dir, "*.xml;*.XML;*.lut;*.LUT")
	while rs.%Next() {
		set tablePath = rs.Get("Name")
		write "Importing: " _ tablePath,!
		// table is the full path, the last part (denoted by *) is the actual file name
		set tablePathNoExtension = $PIECE(tablePath, "/", *)
		// asking for $PIECE with just delimiter asks for the first part, thus ignore anything after the .
		set tablePathNoExtension = $PIECE(tablePathNoExtension, ".")
		write "Importing Lookup Table in " _ tablePathNoExtension,!
		// lookup table should be named the file name (without extension)
		//do ##class(Ens.Util.LookupTable).%ClearTable(tablePathNoExtension)
		
		// Try the new import first.
		set sc = ..ImportLUTFile(tablePath)
			
		// If we got an error, try legacy import
		if $$$ISERR(sc) {
			write "New import failed. Trying legacy import",!
			set sc=##class(Ens.Util.LookupTable).%Import(tablePath)
			if $$$ISOK(sc) {
				write "Import successful",!
			}
		}
		
		// Error remains unfixed. Fail.
		if $$$ISERR(sc) {
			write "Lookup Table import failure: ", $System.Status.GetErrorText(sc),!
			do $system.Process.Terminate(, 1)
		}
	}
}

/// Adapted from EnsPortal.LookupSettings:Import
/// Import lookup tables from file <var>Filename</var>
ClassMethod ImportLUTFile(Filename As %String) As %String
{
	Set tRS = ##class(%ResultSet).%New("%RoutineMgr:ImportItemList")
	Set tSC = tRS.Execute(Filename)
	Quit:$$$ISERR(tSC) tSC
	Set tSC = $$$OK
	Kill Select
	For  {
		Quit:'tRS.Next(.tSC)
		Set Name = tRS.Get("Name")
		If $E(Name,*-3,*)=".LUT" {
			Lock +^Ens.LookupTable(Name):2
			If '$T Set tSC = $$$ERROR($$$LockFailedToAcquireRead,$Name(^Ens.LookupTable(Name))) Quit
			Set Select($E(Name,1,*-4)) = ""
		}
	}
	Quit:$$$ISERR(tSC) tSC
	Quit:'$D(Select) $$$ERROR($$$GeneralError,"This file does not contain any lookup tables")
	Set tSC = $system.OBJ.Load(Filename,"-d", .Err, .Loaded, 0)
	Set Name = ""
	For  {
		Set Name = $O(Select(Name))
		Quit:Name=""
		Lock -^Ens.LookupTable(Name)
	}
	Quit tSC
}
Eduard Lebedyuk · Apr 30, 2024 go to post

Do you have CA cert for the CA used to sign external server's cert?

You need that and not external server's cert itself.

Eduard Lebedyuk · Apr 29, 2024 go to post

Do you want to call a process, the name of which is returned by a method?

You can use indirection @, or assign method result to a local variable and use @ on it.

Eduard Lebedyuk · Apr 27, 2024 go to post

IRIS supports multiple inheritance for methods, so create an abstract class which holds the methods and add it to a list of superclasses of your production class.

Eduard Lebedyuk · Apr 18, 2024 go to post

1. Provision Azure File Share.

2. Mount it locally (on a server with IRIS.DAT)

3. Copy IRIS.DAT into the share.

4. Mount Azure File Share into your cloud env.

5. Move IRIS.DAT from the file share to your block storage.

6. Remove Azure File Share.

Eduard Lebedyuk · Apr 17, 2024 go to post

Longrunning tcp connections might be an issue. I would definitely try setting it to something lower.

Eduard Lebedyuk · Apr 17, 2024 go to post

You need to iterate on value:

 // extract json content from the request:
 set dynRequestJsonPayload = {}.%FromJSON(%request.Content)
 #dim JsonIterator As %Iterator.AbstractIterator
 set JsonIterator = dynRequestJsonPayload.%GetIterator()
 
 // iterate on json structure:
 if dynRequestJsonPayload '= "" {
    while JsonIterator.%GetNext(.key, .value, .NodeType) { 
      if NodeType = "string" {
        do GlobalTrace("NodeType: " _ NodeType _ "; key: " _ key _ "; value: " _ value)
      } elseif NodeType = "array" {
         // i want to iterate on this array...
         // the following line throws the exeception "ERREUR #5002: Erreur ObjectScript: &lt;INVALID OREF&gt;traitementUFI+34^common.REST.1"
         set JsonIteratorSecondary = value.%GetIterator()
      } else {
	      // Do something
      }

    }
}
Eduard Lebedyuk · Apr 16, 2024 go to post

Do you have Stay Connected setting equal to -1? Try setting it to a lower value, i.e. 30.

Eduard Lebedyuk · Mar 29, 2024 go to post

Try this in /bin/iris2/mgr/irisodbc.ini:

[ODBC Data Sources]
HL7Interface2=HL7Interface2

[HL7Interface2]
Driver = /usr/lib64/psqlodbcw.so
Setup=/usr/local/lib/libodbcpsqlS.so
Description = HL7 Interface DB
Servername = localhost
Port = 5432
Protocol = 7.4-1
UserName = postgres
Password =  <real pw here>
Database = hl7interface
ReadOnly = no

And restart iris, after that would it connect?

Eduard Lebedyuk · Mar 6, 2024 go to post

Great article!

I would advice to reuse the client, it will save you a lot of time.

In REST:

ClassMethod init()
{
    If '$data(%JDBCGateway) {
            Set %JDBCGateway("client") = ##class(%SYS.Python).Import("boto3").client("dynamodb")
            Set %JDBCGateway("table") = ..getTable("us-east-2", "mytable")
  }
}

ClassMethod getTable(region, tablename) As %SYS.Python [ Language = python ]
{
    import json
    import boto3
    dynamo = boto3.resource("dynamodb", region_name=region)
    return dynamo.Table(tablename)
}

ClassMethod writepy(table, pk, sk, msg) [ Language = python ]
{
    message_record = {
        "PK": pk,
        "SK": sk,
        "msg": msg
    }
    table.put_item(Item=message_record)
}

And call writepy, passing %JDBCGateway("table")  (or %JDBCGateway("client")).

In interoperability Business Hosts it can look like this:

Class App.BS Extends Ens.BusinessService
{

Parameter ADAPTER = "Ens.InboundAdapter";
Property Adapter As Ens.InboundAdapter;
Property Table As %SYS.Python;
Method OnInit() As %Status
{
    Set ..Table = ##class(App.REST).getTable("region", "table")
    Quit $$$OK
}

}

Also when you're using resource Table instead of client you can use normal JSON and not DynamoDB JSON which makes code more readable and you can also use Dynamic Objects to serialize to json / in python parse it from json to dict and call update.

Eduard Lebedyuk · Mar 6, 2024 go to post

what is the reason of having nested transactions inside the Worker method?

The idea is to check that TCOMMIT works, for additional safety, but yes, inner pair of TSTART/TCOMMIT can be taken out.

And how can you distribute single ("root") transaction execution and control among several processes?

No problems with that, transaction iterates over history data, so it's possible to chunk it.

another approach

Thank you. Locking situation would be better with this approach.

Eduard Lebedyuk · Mar 6, 2024 go to post

That won't rollback transactions inside worker processes (as they might be gone by the point we pass sync).

Eduard Lebedyuk · Mar 4, 2024 go to post

I think two classes in one namespace have a mismatched value of this setting, rather than plainly incorrect value.