Nouvelle publication

Rechercher

Question
· Juin 16, 2023

How to publish 2 SMP on a serveriris

Hi,
i got 2 server with iris instances on them:

srv1
irisinstance1 port 51773/52773
irisinstance2 port 51774/52774

srv2
irisinstance3 port 51773/52773
irisinstance4 port 51774/52774

 

Both of them have apps published on an external apache on port 443 and i would like to publish irisinstance1 and irisinstance2 on port 443 of srv2.

Something like https://srv2/mgmt1/csp/sys/UtilHome.csp and similar to mgmt2.

I've tried with proxypass without luck.

How can i do that? Is there a guide?

 

Thanks!

1 Comment
Discussion (1)1
Connectez-vous ou inscrivez-vous pour continuer
Article
· Juin 16, 2023 10m de lecture

Creating a REST service in IRIS

One of the most common needs of our clients is the creation of REST services that allow access to the information present in IRIS / HealthConnect. The advantage of these REST services is that it allows the development of custom user interfaces with the most current technologies taking advantage of the reliability and performance of IRIS in the back-end.

In today's article we are going to create a web service step by step that will allow us to both store data in our database and later consult them. In addition, we are going to do it by configuring a production that allows us to control the flow of messages that we are receiving at all times and thus be able to control its correct operation.

Before starting, just tell you that you have all the code available on Open Exchange so that you can replicate it as many times as you need, the project is configured in Docker, using the IRIS Community so that you don't have to do anything more than deploy it.

Alright, let's get started.

Environment preparation

Before starting with the configuration of the REST service we must prepare our development environment, we must know what information we are going to receive and what we are going to do with it. For this example we have decided that we are going to receive personal data in the following JSON format:

{
    "PersonId": 1,
    "Name": "Irene",
    "LastName": "Dukas",
    "Sex": "Female",
    "Dob": "01/04/1975"
}

As one of the objectives is to store the information we receive, we are going to create an Objectscript class that allows us to register the information in our IRIS. As you can see, the data is quite simple, so the class will not have much complication:

Class WSTEST.Object.Person Extends %Persistent
{

/// ID of the person
Property PersonId As %Integer;
/// Name of the person
Property Name As %String;
/// Lastname of the person
Property LastName As %String;
/// Sex of the person
Property Sex As %String;
/// DOB of the person
Property Dob As %String;
Index PersonIDX On PersonId [ PrimaryKey ];
}

Perfect, we already have our class defined and we can start working with it.

Creation of our endpoint

Now that we have defined the data class with which we are going to work, it is time to create our Objectscript class that will work as an endpoint that will be called from our front-end. Let's see the example class we have in our project step by step:

Class WSTEST.Endpoint Extends %CSP.REST
{

Parameter HandleCorsRequest = 0;
XData UrlMap [ XMLNamespace = "https://www.intersystems.com/urlmap" ]
{
<Routes>
	<Route Url="/testGet/:pid" Method="GET" Call="TestGet" />
	<Route Url="/testPost" Method="POST" Call="TestPost" />
</Routes>
}

ClassMethod OnHandleCorsRequest(url As %String) As %Status
{
	set url = %request.GetCgiEnv("HTTP_REFERER")
    set origin = $p(url,"/",1,3) // origin = "http(s)://origin.com:port"
    // here you can check specific origins
    // otherway, it will allow all origins (useful while developing only)
	do %response.SetHeader("Access-Control-Allow-Credentials","true")
	do %response.SetHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE,OPTIONS")
	do %response.SetHeader("Access-Control-Allow-Origin",origin)
	do %response.SetHeader("Access-Control-Allow-Headers","Access-Control-Allow-Origin, Origin, X-Requested-With, Content-Type, Accept, Authorization, Cache-Control")
	quit $$$OK
}
// Class method to retrieve the data of a person filtered by PersonId
ClassMethod TestGet(pid As %Integer) As %Status
{
    Try {
        Do ##class(%REST.Impl).%SetContentType("application/json")
        If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
        // Creation of BS instance
        set status = ##class(Ens.Director).CreateBusinessService("WSTEST.BS.PersonSearchBS", .instance)

        // Invocation of BS with pid parameter
        set status = instance.OnProcessInput(pid, .response)
       	if $ISOBJECT(response) {
            // Sending person data to client in JSON format
        	Do ##class(%REST.Impl).%WriteResponse(response.%JSONExport())
		}
        
    } Catch (ex) {
        Do ##class(%REST.Impl).%SetStatusCode("400")
        Do ##class(%REST.Impl).%WriteResponse(ex.DisplayString())
        return {"errormessage": "Client error"}
    }
    Quit $$$OK
}
// Class method to receive person data to persist in our database
ClassMethod TestPost() As %Status
{
    Try {
        Do ##class(%REST.Impl).%SetContentType("application/json")
        If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
        // Reading the body of the http call with the person data
        set bodyJson = %request.Content.Read()
        
        // Creation of BS instance
        set status = ##class(Ens.Director).CreateBusinessService("WSTEST.BS.PersonSaveBS", .instance)
       	#dim response as %DynamicObject
        // Invocation of BS with person data
        set status = instance.OnProcessInput(bodyJson, .response)
        
        if $ISOBJECT(response) {
            // Returning to the client the person object in JSON format after save it
            Do ##class(%REST.Impl).%WriteResponse(response.%JSONExport())
	    }
        
    } Catch (ex) {
        Do ##class(%REST.Impl).%SetStatusCode("400")
        Do ##class(%REST.Impl).%WriteResponse(ex.DisplayString())
        return {"errormessage": "Client error"}
    }
    Quit $$$OK
}

}

Don't worry if it seems unintelligible to you, let's see the most relevant parts of our class:

Class declaration:

Class WSTEST.Endpoint Extends %CSP.REST

As you can see, our WSTEST.Endpoint class extends %CSP.REST, this is necessary to be able to use the class as an endpoint.

Routes definition:

XData UrlMap [ XMLNamespace = "https://www.intersystems.com/urlmap" ]
{
<Routes>
	<Route Url="/testGet/:pid" Method="GET" Call="TestGet" />
	<Route Url="/testPost" Method="POST" Call="TestPost" />
</Routes>
}

In this code snippet we are declaring the routes that can be called from our front-end.

As you can see, we have two declared routes, the first of which will be a GET call in which we will be sent the pid parameter that we will use to search for people by their identifier and that will be managed by the ClassMethod TestGet. The second call will be of the POST type in which we will be sent the information of the person that we have to record in our database and that will be processed by the ClassMethod TestPost.

Let's take a look at both methods:

Retrieving data of a person:

ClassMethod TestGet(pid As %Integer) As %Status
{
    Try {
        Do ##class(%REST.Impl).%SetContentType("application/json")
        If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
        // Creation of BS instance
        set status = ##class(Ens.Director).CreateBusinessService("WSTEST.BS.PersonSearchBS", .instance)

        // Invocation of BS with pid parameter
        set status = instance.OnProcessInput(pid, .response)
       	if $ISOBJECT(response) {
            // Sending person data to client in JSON format
        	Do ##class(%REST.Impl).%WriteResponse(response.%JSONExport())
		}
        
    } Catch (ex) {
        Do ##class(%REST.Impl).%SetStatusCode("400")
        Do ##class(%REST.Impl).%WriteResponse(ex.DisplayString())
        return {"errormessage": "Client error"}
    }
    Quit $$$OK
}

In this method you can see how we have declared in our ClassMethod the reception of the pid attribute that we will use in the subsequent search. Although we could have done the search directly from this class, we have decided to do it within a production to be able to control each of the operations, which is why we are creating an instance of the Business Service WSTEST.BS.PersonSearchBS to which we later call its method OnProcessInput with the received pid. The response that we will receive will be of the WSTEST.Object.PersonSearchResponse type that we will transform into JSON before sending it to the requester.

Storing a person's data:

ClassMethod TestPost() As %Status
{
    Try {
        Do ##class(%REST.Impl).%SetContentType("application/json")
        If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
        // Reading the body of the http call with the person data
        set bodyJson = %request.Content.Read()
        
        // Creation of BS instance
        set status = ##class(Ens.Director).CreateBusinessService("WSTEST.BS.PersonSaveBS", .instance)
       	#dim response as %DynamicObject
        // Invocation of BS with person data
        set status = instance.OnProcessInput(bodyJson, .response)
        
        if $ISOBJECT(response) {
            // Returning to the client the person object in JSON format after save it
            Do ##class(%REST.Impl).%WriteResponse(response.%JSONExport())
	    }
        
    } Catch (ex) {
        Do ##class(%REST.Impl).%SetStatusCode("400")
        Do ##class(%REST.Impl).%WriteResponse(ex.DisplayString())
        return {"errormessage": "Client error"}
    }
    Quit $$$OK
}

As in the previous case, we could have saved our person object directly from this class, but we have decided to do it from a Business Operation that will be called from the Business Service WSTEST.BS.PersonSaveBS.

As you can see in the code we are retrieving the information sent by the client in the POST call by reading the Stream present in %request.Content. The string obtained will be what we will pass to the Business Service.

 

Publishing our Endpoint

To avoid taking this article forever, we are going to ignore the explanation related to production, you can review the code directly in the OpenExchange project. The production that we have configured is as follows:

We have 2 declared Business Services, one to receive search requests and another for storage requests of the person's data. Each of them will invoke their appropriate Business Operation.

Perfect, let's review what we have configured in our project:

  • 1 endpoint class that will receive client requests (WSTest.Endpoint)
  • 2 Business Services that will be called from our endpoint class (WSTest.BS.PersonSaveBS and WSTest.BS.PersonSearchBS).
  • 2 Business Operations in charge of carrying out the search and recording of the data (WSTest.BS.PersonSaveBS and WSTest.BS.PersonSearchBS)
  • 4 classes to send and receive data within the production that extend from Ens.Request and Ens.Response (WSTest.Object.PersonSearchRequestWSTest.Object.PersonSaveRequestWSTest.Object.PersonSearchResponse WSTest.Object.PersonSaveResponse).

We only have one last step left to put our web service into operation and that is its publication. To do this, we will access the Management Portal option System Administration -->  Security -> Applications -> Web Applications

We will see a list of all the Web Applications configured in our instance:

Let's create our web application:

Let's go over the points we need to configure:

  • Name: We will define the route that we are going to use to make the invocations to our service, for our example it will be /csp/rest/wstest
  • Namespace: the namespace on which the web service will work, in this case we have created WSTEST in which we have configured our production and our classes.
  • Enable Application: enabling the web service to be able to receive calls.
  • Enable - REST: When selecting REST we indicate that this web service is configured to receive REST calls, when selecting it we must define the Dispatch Class that will be our endpoint class WSTEST.Endpoint.
  • Allowed Authentication Methods: configuration of the authentication of the user who makes the call to the web service. In this case we have defined that it be done through Password, so in our Postman we will configure the Basic Authorization mode in which we will indicate the username and password. We have the option of defining the use of JWT Authentication which is quite useful as it does not expose the user data and password in REST calls, if you are interested in delving into JWT you can consult this article.

Once we finish configuring our web application we can launch a couple of tests by opening Postman and importing the WSTest.postman_collection.json file present in the project.

Testing our Endpoint

With everything configured in our project and production started, we can launch a couple of tests on our web service. We have superuser configured as the requesting user so we won't have problems in order to save and retrieve data. In case of using a different user, we must make sure that either he has the necessary roles assigned or we assign them in the Application Roles tab of the definition of our web application.

Let's start by recording someone in our database:

We have received a 200, so it seems that everything went well, let's check the message in production:

Everything has gone well, the message with the JSON has been correctly received in the BS and has been successfully recorded in the BO.

Now let's try to recover the data of our beloved Alexios Kommenos:

Bingo! There is Alexios with all the information about him. Let's check the message in production:

Perfect, everything has worked as it should. As you have seen, it is really easy to create a REST web service in IRIS, you can use this project as a base for your future developments and if you have any questions, don't hesitate to leave a comment.

Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer
Question
· Juin 15, 2023

Newbie question on calculate size of X12 messages.

How do I determine the size of an X12 message within the Rule Editor?  I should add this is for HealthConnect or Ensemble.

The FullSize property always shows 122, and the FullSizeGet method isn't available within the Rule Editor.

Our vendor can't handle messages over 500,000 characters, so I need to write those to another queue for later processing, and to get the interface into they're system from crashing.

Thanks,

2 Comments
Discussion (2)1
Connectez-vous ou inscrivez-vous pour continuer
Article
· Juin 15, 2023 6m de lecture

LangChain InterSystems PDF to Interview Questions and FlashCards

Demonstration example for the current Grand Prix contest for use of a more complex Parameter template to test the AI.

Interview Questions

There is documentation. A recruitment consultant wants to quickly challenge candidates with some relevant technical questions to a role.

Can they automate making a list of questions and answers from the available documentation?

Interview Answers and Learning

One of the most effective ways to cement new facts into accessible long term memory is with phased recall.

In essence you take a block of text information, reorganize it into a series of self-contained Questions and Facts.

Now imagine two questions:

  • What day of the week is the trash-bin placed outside for collection?
  • When is the marriage anniversary?

Quickly recalling correct answers can mean a happier life!!

Recalling the answer to each question IS the mechanism to enforce a fact into memory.

Phased Recall re-asks each question with longed and longer time gaps when the correct answer is recalled.
For example:

  • You consistently get the right answer: The question is asked again tomorrow, in 4 days, in 1 week, in 2 weeks, in 1 month.
  • You consistently get the answer wrong: The question will be asked every day until it starts to be recalled.

If you can easily see challenging answers, it is productive to re-work difficult answers, to make them more memorable.

There is a free software package called Anki that provides this full phased recall process for you.

If you can automate the creation of questions and answers into a text file, the Anki will create new flashcards for you.

Hypothesis

We can use LangChain to transform InterSystems PDF documentation into a series of Questions and answers to:

  • Make interview questions and answers
  • Make Learner Anki flash cards

Create new virtual environment

mkdir chainpdf

cd chainpdf

python -m venv .

scripts\activate 

pip install openai
pip install langchain
pip install wget
pip install lancedb
pip install tiktoken
pip install pypdf

set OPENAI_API_KEY=[ Your OpenAI Key ]

python

Prepare the docs

import glob
import wget;

url='https://docs.intersystems.com/irisforhealth20231/csp/docbook/pdfs.zip';
wget.download(url)
# extract docs
import zipfile
with zipfile.ZipFile('pdfs.zip','r') as zip_ref:
  zip_ref.extractall('.')

Extract PDF text

from langchain.document_loaders import PyPDFLoader
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.prompts.prompt import PromptTemplate
from langchain import OpenAI
from langchain.chains import LLMChain

# To limit for the example
# From the documentation site I could see that documentation sets
# GCOS = Using ObjectScript
# RCOS = ObjectScript Reference
pdfFiles=['./pdfs/pdfs/GCOS.pdf','./pdfs/pdfs/RCOS.pdf']

# The prompt will be really big and need to leave space for the answer to be constructed
# Therefore reduce the input string
text_splitter = CharacterTextSplitter(
    separator = "\n\n",
    chunk_size = 200,
    chunk_overlap  = 50,
    length_function = len,
)

# split document text into chuncks
documentsAll=[]
for file_name in pdfFiles:
  loader = PyPDFLoader(file_name)
  pages = loader.load_and_split()
  # Strip unwanted padding
  for page in pages:
    del page.lc_kwargs
    page.page_content=("".join((page.page_content.split('\xa0'))))
  documents = text_splitter.split_documents(pages)
  # Ignore the cover pages
  for document in documents[2:]:
    # skip table of contents
    if document.page_content.__contains__('........'):
      continue
    documentsAll.append(document)

Prep search template

_GetDocWords_TEMPLATE = """From the following documents create a list of distinct facts.
For each fact create a concise question that is answered by the fact.
Do NOT restate the fact in the question.

Output format:
Each question and fact should be output on a seperate line delimited by a comma character
Escape every double quote character in a question with two double quotes
Add a double quote to the beginning and end of each question
Escape every double quote character in a fact with two double quotes
Add a double quote to the beginning and end of each fact
Each line should end with {labels}

The documents to reference to create facts and questions are as follows:
{docs}
"""

PROMPT = PromptTemplate(
     input_variables=["docs","labels"], template=_GetDocWords_TEMPLATE
)

llm = OpenAI(temperature=0, verbose=True)
chain = LLMChain(llm=llm, prompt=PROMPT)

Process each document and place output in file

# open an output file
with open('QandA.txt','w') as file:
  # iterate over each text chunck
  for document in documentsAll:
    # set the label for Anki flashcard
    source=document.metadata['source']
    if source.__contains__('GCOS.pdf'):
      label='Using ObjectScript'
    else:
      label='ObjectScript Reference'
    output=chain.run(docs=document,labels=label)
    file.write(output+'\n')
    file.flush()

 

There were some retry and force-close messages during loop.

Anticipate this is limiting the openAI API to a fair use.

Alternatively a local LLM could be applied instead.

Examine the output file

"What are the contexts in which ObjectScript can be used?", "You can use ObjectScript in any of the following contexts: Interactively from the command line of the Terminal, As the implementation language for methods of InterSystems IRIS object classes, To create ObjectScript routines, and As the implementation language for Stored Procedures and Triggers within InterSystems SQL.", Using ObjectScript,
"What is a global?", "A global is a sparse, multidimensional database array.", Using ObjectScript,
"What is the effect of the ##; comment on INT code line numbering?", "It does not change INT code line numbering.", Using ObjectScript,
"What characters can be used in an explicit namespace name after the first character?", "letters, numbers, hyphens, or underscores", Using ObjectScript
"Are string equality comparisons case-sensitive?", "Yes" Using ObjectScript,
"What happens when the number of references to an object reaches 0?", "The system automatically destroys the object.",Using ObjectScript
Question: "What operations can take an undefined or defined variable?", Fact: "The READ command, the $INCREMENT function, the $BIT function, and the two-argument form of the $GET function.", Using ObjectScript,  a

While a good attempt at formatting answers has occurred there is some deviation.

Manually reviewing I can pick some questions and answers to continue the experiment.

Importing FlashCards into Anki

Reviewed text file:

"What are the contexts in which ObjectScript can be used?", "You can use ObjectScript in any of the following contexts: Interactively from the command line of the Terminal, As the implementation language for methods of InterSystems IRIS object classes, To create ObjectScript routines, and As the implementation language for Stored Procedures and Triggers within InterSystems SQL.", "Using ObjectScript",
"What is a global?", "A global is a sparse, multidimensional database array.", "Using ObjectScript",
"What is the effect of the ##; comment on INT code line numbering?", "It does not change INT code line numbering.", "Using ObjectScript",
"What characters can be used in an explicit namespace name after the first character?", "letters, numbers, hyphens, or underscores", "Using ObjectScript"
"Are string equality comparisons case-sensitive?", "Yes", "Using ObjectScript",
"What happens when the number of references to an object reaches 0?", "The system automatically destroys the object.","Using ObjectScript"
"What operations can take an undefined or defined variable?", "The READ command, the $INCREMENT function, the $BIT function, and the two-argument form of the $GET function.", "Using ObjectScript"

Creating new Anki card deck

Open Anki and select File -> Import

 

Select the reviewed text file

Optionally create a new Card Deck for "Object Script"

A basic card type is fine for this format

 

There was mention of a "Field 4" so should check the records.

Anki import success

Lets Study

Now choose the reinforcement schedule

Happy Learning !!

References

Anki software is available from https://apps.ankiweb.net/

Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer
Veuillez noter que cette publication est obsolète.
InterSystems officiel
· Juin 14, 2023

2023 年 6月 13 日 - 勧告:プロセスメモリ使用量の増加

インターシステムズは、InterSystems IRIS 製品でプロセスメモリの使用量が増加する不具合を修正しました。

 

対象バージョン: 
  InterSystems IRIS                      2022.2, 2022.3, 2023.1.0
  InterSystems IRIS for Health   2022.2, 2022.3, 2023.1.0
  HealthShare Health Connect   2022.2, 2022.3, 2023.1.0
  Healthcare Action Engine         2022.1


  
対象プラットフォーム: すべて

 

問題の詳細:
ローカル変数に対して $Order$Query または Merge を実行する際に、プロセスのローカル変数テーブルのメモリ消費量の増加が発生します。 この問題は、ほとんどの実行環境では悪影響を与えませんが、プロセス数が多い環境、またはプロセス当たりの最大メモリを厳密に制限している環境では、影響を受ける可能性があります。 また、一部のプロセスで <STORE>エラーが発生する場合があります。

 

解決方法:
この問題は修正 ID : DP-423127 および DP-423237 で解決します。
これらの修正は、今後のすべてのバージョンに含まれる予定です。 

また、既に公開されていた InterSystems IRIS 2023.1.0.229.0 はこの修正を含むバージョン InterSystems IRIS 2023.1.0.235.1 に更新されました。
 
お客様のご要望により、修正を現在お使いの EM リリースの製品に対するパッチとして個別に作成してご提供することが可能です。お使いのシステムに対するパッチが必要な場合は、バージョン情報とライセンスキー情報をご確認の上インターシステムズカスタマーサポートセンターまでお知らせ下さい。この勧告について質問がある場合は、インターシステムズカスタマーサポートセンターまでご連絡下さい。

Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer