go to post Guillaume Rongier · Mar 15 Once again, can we wrote a wrapper class for JDBC driver, eg : com.intersystems.dc.jdbc.IRISJDBCDriver, and use it to connect to IRIS? The wrapper can be a public repository, and published to maven central, so that it can be used by anyone. Like that no conflict with the official driver, and we can use it in any project. Downside is that the name is not the same, so we need to change the driver name in the code, but it's a small price to pay.
go to post Guillaume Rongier · Mar 15 Hi, As you are in Aync mode, it's a bit tricky to get the end time. In sync mode, you just have to wait for the answer to be back. In this case I would use the session id from the message that is going out of the business service to the BO. So in the BS, try something like this: Method OnProcessInput( pDocIn As %RegisteredObject, Output pDocOut As %RegisteredObject) As %Status { // here you need to pass the jobid from the %CSP.REST that is invoking the BS set tJobId = pDocIn.JobID // your code here set ^CallApi($CLASSNAME(),tJobId,"sessionid") = ..%SessionId set pDocOut = pDocIn quit $$$OK } Then in the BO, you can use the session id to get the information from the global. Method MyAsyncMethod( pRequest As %RegisteredObject, Output pResponse As %RegisteredObject) As %Status { set tSessionId = ..%SessionId // lookup for the good session id for { set tClassname = $ORDER(^CallApi(tClassname)) quit:tClassname="" for { set tJobId = $ORDER(^CallApi(tClassname,tJobId)) quit:tJobId="" if ^CallApi(tClassname,tJobId,"sessionid") = tSessionId { set ^CallApi(tClassname,tJobId,"End") = $HOROLOG quit } } } // your code here quit $$$OK } I hope this helps.
go to post Guillaume Rongier · Mar 14 Maybe a dumb idea, why not create a community edition of those driver as they are already publish on https://github.com/intersystems-community/iris-driver-distribution. It's already done for docker : https://hub.docker.com/r/intersystemsdc/iris-community Why not for maven ?
go to post Guillaume Rongier · Mar 14 Thanks @Corentin Blondeau and @Amaury Hashemi for sharing code/ideas and tools.
go to post Guillaume Rongier · Feb 29 thanks @Ron Sweeney, I take good note of your tips for debugging, because it can become quickly a mess with too much info in ^ISCLOG
go to post Guillaume Rongier · Feb 13 Thank you for your kind words, I'm glad you liked the article. I've read your article and it's a great use case of Embedded Python in IRIS. I noticed that you are making an extensive use of the language tags ( [ language = python ])in your article. In this article, I try to explain how I try to move away from the language tags and instead stick with only ObjectScript code and use the ##class(%SYS.Python).Import() method to call Python code from ObjectScript or even have a python first approach. Since you are using the language tags, I'm curious to know if you have tried the ##class(%SYS.Python).Import() method or a python first approach and if so, what are the advantages and disadvantages of using the language tags over the ##class(%SYS.Python).Import() method and the python first approach? Are you agree with me on all the drawbacks of using the language tags ( it's not Pythonic, it's not ObjectScript either, you don't have a debugger, you don't have a linter, you mixing two language in the same file, when you process crashes, you don't have a stack trace, ... ) ?
go to post Guillaume Rongier · Feb 8 Ok, I see your point and I have a better understanding of your first question. The issue here is that the Python interpreter is not able to find the module hello_world because it is not in the Python path. The Python path is a list of directories that the interpreter searches to find the modules that you import. By default, the Python path in embedded python is /<install dir>/lib/python. To solve this issue, you can add the directory containing the hello_world module to the Python path. You can do this by adding the directory to the sys.path list in Python. Here is an example of how you can do this: Class dc.PythonProxy Extends %RegisteredObject { Property PythonPath As %String; Property PythonModule As %String; Property PythonClassname As %String; Property PythonClass As %SYS.Python; ClassMethod SetPythonPath(pClasspaths) { set sys = ##class(%SYS.Python).Import("sys") // avoid adding the same path multiple times for i=0:1:(sys.path."__len__"()-1) { Try { if sys.path."__getitem__"(i) = pClasspaths { do sys.path."__delitem__"(i) } } Catch ex { // do nothing } } do sys.path.insert(0, pClasspaths) } ClassMethod GetPythonInstance( pModule, pRemoteClassname) As %SYS.Python { set importlib = ##class(%SYS.Python).Import("importlib") set builtins = ##class(%SYS.Python).Import("builtins") set module = importlib."import_module"(pModule) do importlib."reload"(module) set class = builtins.getattr(module, pRemoteClassname) return class."__new__"(class) } Method %OnNew(pPythonPath,pPythonModule,pPythonClassname) As %Status { // set variables set ..PythonPath = pPythonPath set ..PythonModule = pPythonModule set ..PythonClassname = pPythonClassname // Then set the python class do ..SetPythonPath(..PythonPath) set ..PythonClass = ..GetPythonInstance(..PythonModule, ..PythonClassname) quit $$$OK } } With this class you have a generic way to import any python module and class. You can use it like this: Set pythonProxy = ##class(dc.PythonProxy).%New("/iris-shared/python", "hello_world", "HelloWorld") Write pythonProxy.PythonClass.sayhello() This will add the /iris-shared/python directory to the Python path and then import the hello_world module and create an instance of the HelloWorld class. With the language tag you can do something like this: Class dc.boto Extends %RegisteredObject { ClassMethod test1() [ Language = python ] { # set path import sys # check if the path is already in the sys.path if "/iris-shared/python" not in sys.path: sys.path.insert(0, "/iris-shared/python") # import module import hello_world greeting = hello_world.HelloWorld() # Call the say_hello method ret=greeting.sayhello() print(ret) } } I hope this helps! Let me know if you have any other questions.
go to post Guillaume Rongier · Feb 7 Many thanks for your article, Heloisa! I guess that we have the same goal: to make IRIS more accessible to Python developers. Do you know that IRIS 2024.1 will have a native support of WSGI :) I'm looking forward to your next article! If you have time can you have a look at my article about feedback using embedded python daily for more than 2 years and tell me what do you think about it?
go to post Guillaume Rongier · Feb 7 Thanks @Mario Sanchez Macias for your feedback. I understand your concerns and will address them in this response. The example I provided is indeed complex, and I appreciate your patience. I will provide a simpler example to illustrate the integration of Python with ObjectScript. First, I will give you a quick tip on how to simplify your code without the need of importing the same libraries repeatedly. In ObjectScript, you create a class like this: Class dc.boto3 Extends %RegisteredObject { XData %import [ MimeType = application/python ] { import boto3 } ClassMethod moveFile() [ Language = python ] { boto3.client('s3').download_file('bucket', 'key', 'filename') # do stuff } ClassMethod getFile() [ Language = python ] { boto3.client('s3').upload_file('filename', 'bucket', 'key') # do stuff } } In this example, I've created a class called dc.boto3 that extends %RegisteredObject. I've added an XData block to import the boto3 library. This way, you don't need to import the library in each method. But in this example you are still mixing Python and ObjectScript code. You can also try to stick to only to ObjectScript and use the ##class(%SYS.Python).Import method to import the Python library and use it in your ObjectScript code. Here's an example of how you can use it to import the boto3 library and use it in your ObjectScript code: Class dc.boto3 Extends %RegisteredObject { ClassMethod moveFile() { set boto3 = ##class(%SYS.Python).Import("boto3") set s3 = boto3.client('s3') do s3."download_file"('bucket', 'key', 'filename') # do stuff } ClassMethod getFile() { set boto3 = ##class(%SYS.Python).Import("boto3") set s3 = boto3.client('s3') do s3."upload_file"('filename', 'bucket', 'key') # do stuff } } Now, to move to a python only approach, this will depend on your application architecture and the complexity of your Python code. First, do you use the Interoperability framework of Iris ? If yes, it will be quite easy have a look at IoP, it will allow you to create Services, Processes, Operations in Python Only and call them from a Production. Your entry point is %CSP.Rest application ? You can start to create .py files and import them in your ObjectScript code. with the ##class(%SYS.Python).Import method. Create an ObjectScript abstract class that will be used to call your Python code. This is my favorite approach, but the most complex one. This is the one I try to explain in the article. If we take the example of the boto3 library. First create the abstract class in ObjectScript: Class dc.boto3 Extends %RegisteredObject [ Abstract ] { ClassMethod moveFile() { Quit $$$ERROR($$$NOTIMPLEMENTED) } ClassMethod getFile() { Quit $$$ERROR($$$NOTIMPLEMENTED) } } The great thing about this approach is that you can create a class that extends this abstract class and implement the methods in Python, ObjectScript or any other language. Then create the implementation of this class in ObjectScript: Class dc.boto3Python Extends dc.boto3 { Property PythonPath As %String; Property PythonClassname As %String; Property PythonModule As %String; Property PythonClass As %SYS.Python; Method %OnNew() As %Status { // %OnNew is called when the object is created. set ..PythonPath = $system.Util.GetEnviron("PYTHON_BOTO_PATH") // Then set the python class name from the env var set ..PythonClassname = $system.Util.GetEnviron("PYTHON_BOTO_CLASS") // Then set the python module name from the env var set ..PythonModule = $system.Util.GetEnviron("PYTHON_BOTO_MODULE") if (..PythonPath = "") || (..PythonClassname = "") || (..PythonModule = "") { //quit ##super(pStrategy) set ..PythonPath = "/irisdev/app/src/python/" set ..PythonClassname = "CustomPythonBoto" set ..PythonModule = "custom" } // Then set the python class do ..SetPythonPath(..PythonPath) set ..PythonClass = ##class(FHIR.Python.Interactions).GetPythonInstance(..PythonModule, ..PythonClassname) quit ##super(pStrategy) } ClassMethod moveFile() { do ..PythonClass."moveFile"() } ClassMethod getFile() { do ..PythonClass."getFile"() } } Now you can create a Python class that is used to implement the methods of the dc.boto3Python class. import boto3 class CustomPythonBoto: def moveFile(self): s3 = boto3.client('s3') s3.download_file('bucket', 'key', 'filename') # do stuff def getFile(self): s3 = boto3.client('s3') s3.upload_file('filename', 'bucket', 'key') # do stuff Now anone in your company can use the dc.boto3Python class as it's an ObjectScript class. But in fact, it's proxying the Python class CustomPythonBoto. I hope this helps. Let me know if you have any further questions.
go to post Guillaume Rongier · Feb 6 Hi Henrique and José, I love your project and the idea behind it. I'm wondering if you have any plans to integrate IRIS-FHIRfy with IoP (Interoperability on Python) ? Why ? Because you project at the end creates a lot of Python code that can be used and wrapped in IoP. May be it's just need to update your prompt to include some notion of IoP and we can end up with a turnkey solution with ready to use code with IoP and IRIS interoperability.
go to post Guillaume Rongier · Dec 22, 2023 true, it's a mistake in the readme, the port is 8051, not 8501.
go to post Guillaume Rongier · Dec 20, 2023 have you give a try to this EAP program ? https://community.intersystems.com/post/health-data-de-id-early-access-p...
go to post Guillaume Rongier · Dec 18, 2023 Great features, is possible to pass also an entrypoint script ? Or is it only for vanilla versions ?
go to post Guillaume Rongier · Dec 1, 2023 Hi, you can also try iris-magic : https://github.com/grongierisc/ipython-iris-magic also available on pypi : https://pypi.org/project/ipython-iris-magic/
go to post Guillaume Rongier · Nov 30, 2023 What matters is more the maturity of each resource. Maturity is rated from 0 to 5, where 0 is a draft, and 5 is normative. Further the realse of FHIR, more mature resources are. It's plan than R6 will be the first normative release of FHIR, it's even plan to be an ISO standard. This mean that all resources related to Patient will be normative, and will not change in the future.
go to post Guillaume Rongier · Nov 24, 2023 yes, it should be compatible, iris support Prometheus api format And Prometheus api format are a sub set of opentelemetry. https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls... watchout, from what i have understand opentelemetry are for metrics, not for logs.
go to post Guillaume Rongier · Nov 24, 2023 I haven't tried it yet on 2021.2, but on 2023.1 you use case is working : IRISAPP>w ##class(Zpy.Utility).Info() def IRISAPP>zn "USER" USER>w ##class(Zpy.Utility).Info() def USER>zw $zv "IRIS for UNIX (Ubuntu Server LTS for x86-64 Containers) 2023.1 (Build 229U) Fri Apr 14 2023 17:37:52 EDT" Can you update iris ? If you can't try to map rPYC global to the USER namespace. rPYC global actually contains the python code.
go to post Guillaume Rongier · Nov 20, 2023 Hello Cyril, I have finished this exercise, I will share my experience on this subject with you: Have you checked that the snmpd deamon is installed and configured on your docker instance? By default, it is not installed, so you have to install and configure it. Example of a dockerfile to install snmpd: ARG IMAGE=intersystemsdc/iris-community:latest FROM $IMAGE WORKDIR /irisdev/app USER root RUN apt-get update && apt-get install -y \ nano \ snmpd \ snmp \ sudo && \ /bin/echo -e ${ISC_PACKAGE_MGRUSER}\\tALL=\(ALL\)\\tNOPASSWD: ALL >> /etc/sudoers && \ sudo -u ${ISC_PACKAGE_MGRUSER} sudo echo enabled passwordless sudo-ing for ${ISC_PACKAGE_MGRUSER} COPY snmpd.conf /etc/snmp/snmpd.conf USER ${ISC_PACKAGE_MGRUSER} Example of a snmpd.conf: ############################################################################### # # snmpd.conf: # An example configuration file for configuring the NET-SNMP agent with Cache. # # This has been used successfully on Red Hat Enterprise Linux and running # the snmpd daemon in the foreground with the following command: # # /usr/sbin/snmpd -f -L -x TCP:localhost:705 -c./snmpd.conf # # You may want/need to change some of the information, especially the # IP address of the trap receiver of you expect to get traps. I've also seen # one case (on AIX) where we had to use the "-C" option on the snmpd command # line, to make sure we were getting the correct snmpd.conf file. # ############################################################################### ########################################################################### # SECTION: System Information Setup # # This section defines some of the information reported in # the "system" mib group in the mibII tree. # syslocation: The [typically physical] location of the system. # Note that setting this value here means that when trying to # perform an snmp SET operation to the sysLocation.0 variable will make # the agent return the "notWritable" error code. IE, including # this token in the snmpd.conf file will disable write access to # the variable. # arguments: location_string syslocation "System Location" # syscontact: The contact information for the administrator # Note that setting this value here means that when trying to # perform an snmp SET operation to the sysContact.0 variable will make # the agent return the "notWritable" error code. IE, including # this token in the snmpd.conf file will disable write access to # the variable. # arguments: contact_string syscontact "Your Name" # sysservices: The proper value for the sysServices object. # arguments: sysservices_number sysservices 76 ########################################################################### # SECTION: Agent Operating Mode # # This section defines how the agent will operate when it # is running. # master: Should the agent operate as a master agent or not. # Currently, the only supported master agent type for this token # is "agentx". # # arguments: (on|yes|agentx|all|off|no) master agentx agentXSocket tcp:localhost:705 ########################################################################### # SECTION: Trap Destinations # # Here we define who the agent will send traps to. # trapsink: A SNMPv1 trap receiver # arguments: host [community] [portnum] trapsink localhost public ############################################################################### # Access Control ############################################################################### # As shipped, the snmpd demon will only respond to queries on the # system mib group until this file is replaced or modified for # security purposes. Examples are shown below about how to increase the # level of access. # # By far, the most common question I get about the agent is "why won't # it work?", when really it should be "how do I configure the agent to # allow me to access it?" # # By default, the agent responds to the "public" community for read # only access, if run out of the box without any configuration file in # place. The following examples show you other ways of configuring # the agent so that you can change the community names, and give # yourself write access to the mib tree as well. # # For more information, read the FAQ as well as the snmpd.conf(5) # manual page. # #### # First, map the community name "public" into a "security name" # sec.name source community com2sec notConfigUser default public #### # Second, map the security name into a group name: # groupName securityModel securityName group notConfigGroup v1 notConfigUser group notConfigGroup v2c notConfigUser #### # Third, create a view for us to let the group have rights to: # Make at least snmpwalk -v 1 localhost -c public system fast again. # name incl/excl subtree mask(optional) # access to 'internet' subtree view systemview included .1.3.6.1 # access to Cache MIBs Caché and Ensemble view systemview included .1.3.6.1.4.1.16563.1 view systemview included .1.3.6.1.4.1.16563.2 #### # Finally, grant the group read-only access to the systemview view. # group context sec.model sec.level prefix read write notif access notConfigGroup "" any noauth exact systemview none none From this neat article : https://community.intersystems.com/post/intersystems-data-platforms-and-... Then, you have to start the snmpd deamon: sudo service snmpd start On iris, you have to configure the snmp agent: %SYS> w $$start^SNMP() With all these steps, you should be able to retrieve information via snmp. snmpwalk -m ALL -v 2c -c public localhost .iso.3.6.1.4.1.16563.4.1.1.1.5.4.73.82.73.83 Result : iso.3.6.1.4.1.16563.4.1.1.1.5.4.73.82.73.83 = STRING: "IRIS for UNIX (Ubuntu Server LTS for x86-64 Containers) 2023.1 (Build 229U) Fri Apr 14 2023 17:37:52 EDT" Now that the snmpd service is running and functional as well as the snmp agent. I encourage you to have a look at OpenTelemetry, it's a new standard for monitoring and tracing. https://opentelemetry.io/ And our implementation of OpenTelemetry for IRIS : https://docs.intersystems.com/iris20233/csp/docbook/Doc.View.cls?KEY=GCM...