go to post Guillaume Rongier · Aug 7, 2023 In theory you are all set. Just take for example the dockerfile. You can also read the pom.xml file to take some insperation. For example take a look how the jdbc driver is added to the project. <dependency> <groupId>intersystems</groupId> <artifactId>jdbc</artifactId> <version>3.3.0</version> <scope>system</scope> <systemPath>${pom.basedir}/lib/intersystems-jdbc-3.3.0.jar</systemPath> </dependency> To sum up, you need obviously the jdbc drive (you have one in the repo), the hibernate dialect, you also have one in the repo, for the hibernete dialet you can also have a look to the article of yuri : https://community.intersystems.com/post/using-new-intersystems-iris-hibe.... Have fun with Iris and quarkus.
go to post Guillaume Rongier · Aug 7, 2023 Hi, you will find here an example of Quarkus + IRIS + Hibernete : https://github.com/grongierisc/iris-orm-examples
go to post Guillaume Rongier · Jul 27, 2023 Thanks and fixed. BTW, now the new version of this project is in python : import time import os import json import iris from FhirInteraction import Interaction, Strategy, OAuthInteraction from google.oauth2 import id_token from google.auth.transport import requests import requests as rq # The following is an example of a custom OAuthInteraction class that class CustomOAuthInteraction(OAuthInteraction): client_id = None last_time_verified = None time_interval = 5 def clear_instance(self): self.token_string = None self.oauth_client = None self.base_url = None self.username = None self.token_obj = None self.scopes = None self.verify_search_results = None def set_instance(self, token:str,oauth_client:str,base_url:str,username:str): self.clear_instance() if not token or not oauth_client: # the token or oauth client is not set, skip the verification return global_time = iris.gref('^FHIR.OAuth2.Time') if global_time[token[0:50]]: self.last_time_verified = global_time[token[0:50]] if self.last_time_verified and (time.time() - self.last_time_verified) < self.time_interval: # the token was verified less than 5 seconds ago, skip the verification return self.token_string = token self.oauth_client = oauth_client self.base_url = base_url self.username = username # try to set the client id try: # first get the var env GOOGLE_CLIENT_ID is not set then None self.client_id = os.environ.get('GOOGLE_CLIENT_ID') # if not set, then by the secret.json file if not self.client_id: with open(os.environ.get('ISC_OAUTH_SECRET_PATH'),encoding='utf-8') as f: data = json.load(f) self.client_id = data['web']['client_id'] except FileNotFoundError: pass try: self.verify_token(token) except Exception as e: self.clear_instance() raise e # token is valid, set the last time verified to now global_time[token[0:50]]=time.time() def verify_token(self,token:str): # check if the token is an access token or an id token if token.startswith('ya29.'): self.verify_access_token(token) else: self.verify_id_token(token) def verify_access_token(self,token:str): # verify the access token is valid # get with a timeout of 5 seconds response = rq.get(f"https://www.googleapis.com/oauth2/v3/tokeninfo?access_token={token}",timeout=5) try: response.raise_for_status() except rq.exceptions.HTTPError as e: # the token is not valid raise e def verify_id_token(self,token:str): # Verify the token and get the user info idinfo = id_token.verify_oauth2_token(token, requests.Request(), self.client_id) if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']: raise ValueError('Wrong issuer.') def get_introspection(self)->dict: return {} def get_user_info(self,basic_auth_username:str,basic_auth_roles:str)->dict: return {"Username":basic_auth_username,"Roles":basic_auth_roles} def verify_resource_id_request(self,resource_type:str,resource_id:str,required_privilege:str): pass def verify_resource_content(self,resource_dict:dict,required_privilege:str,allow_shared_resource:bool): pass def verify_history_instance_response(self,resource_type:str,resource_dict:dict,required_privilege:str): pass def verify_delete_request(self,resource_type:str,resource_id:str,required_privilege:str): pass def verify_search_request(self, resource_type:str, compartment_resource_type:str, compartment_resource_id:str, parameters:'iris.HS.FHIRServer.API.Data.QueryParameters', required_privilege:str): pass def verify_system_level_request(self): pass class CustomStrategy(Strategy): def on_get_capability_statement(self, capability_statement): # Example : del resources Account capability_statement['rest'][0]['resource'] = [resource for resource in capability_statement['rest'][0]['resource'] if resource['type'] != 'Account'] return capability_statement class CustomInteraction(Interaction): def on_before_request(self, fhir_service, fhir_request, body, timeout): #Extract the user and roles for this request #so consent can be evaluated. self.requesting_user = fhir_request.Username self.requesting_roles = fhir_request.Roles def on_after_request(self, fhir_service, fhir_request, fhir_response, body): #Clear the user and roles between requests. self.requesting_user = "" self.requesting_roles = "" def post_process_read(self, fhir_object): #Evaluate consent based on the resource and user/roles. #Returning 0 indicates this resource shouldn't be displayed - a 404 Not Found #will be returned to the user. return self.consent(fhir_object['resourceType'], self.requesting_user, self.requesting_roles) def post_process_search(self, rs, resource_type): #Iterate through each resource in the search set and evaluate #consent based on the resource and user/roles. #Each row marked as deleted and saved will be excluded from the Bundle. rs._SetIterator(0) while rs._Next(): if not self.consent(rs.ResourceType, self.requesting_user, self.requesting_roles): #Mark the row as deleted and save it. rs.MarkAsDeleted() rs._SaveRow() def consent(self, resource_type, user, roles): #Example consent logic - only allow users with the role '%All' to see #Observation resources. if resource_type == 'Observation': if '%All' in roles: return True else: return False else: return True Based on https://community.intersystems.com/post/iris-fhir-python-strategy
go to post Guillaume Rongier · Jul 13, 2023 That how i read Stream and Write stream with Embedded Python : Read Stream : def stream_to_string(stream)-> str: string = "" stream.Rewind() while not stream.AtEnd: string += stream.Read(1024) return string Write Stream : def string_to_stream(string:str): stream = iris.cls('%Stream.GlobalCharacter')._New() n = 1024 chunks = [string[i:i+n] for i in range(0, len(string), n)] for chunk in chunks: stream.Write(chunk) return stream
go to post Guillaume Rongier · Jul 13, 2023 Here is an example code to modify or create a production XData block, i guess you can adapt to you needs : ClassMethod CreateProduction( package As %String = "test", name As %String = "AutoCreatedProduction", xdata As %CharacterStream) As %Status { #Dim produtionClassName As %String = package _ "." _ name If ('$ZName(produtionClassName, 4)) { Return $System.Status.Error(5001, "Invalid Production package or name.") } #Dim productionDefinition As %Dictionary.ClassDefinition // Check if the production already exists If (##class(%Dictionary.ClassDefinition).%ExistsId(produtionClassName)) { // Open the production set productionDefinition = ##class(%Dictionary.ClassDefinition).%OpenId(produtionClassName) } Else { // Create the production definition set productionDefinition = ##Class(%Dictionary.ClassDefinition).%New() } // Set productionDefinition.Name = produtionClassName Set productionDefinition.Super = "Ens.Production" Set productionDefinition.ClassVersion = 25 // // Check if the XData Definition already exists If (##Class(%Dictionary.XDataDefinition).%ExistsId(produtionClassName_"||ProductionDefinition")) { // delete the XData Definition $$$ThrowOnError(##Class(%Dictionary.XDataDefinition).%DeleteId(produtionClassName_"||ProductionDefinition")) } #Dim xdataDefinition As %Dictionary.XDataDefinition = ##Class(%Dictionary.XDataDefinition).%New() // Set xdataDefinition.Name = "ProductionDefinition" // Do xdataDefinition.Data.CopyFrom(xdata) // // Insert XData Definition into Production Definition Do productionDefinition.XDatas.Insert(xdataDefinition) // #Dim statusCode As %Status = productionDefinition.%Save() // If ($System.Status.IsError(statusCode)) { Return statusCode } // Compile the production class return $System.OBJ.Compile(produtionClassName,"k-d") }
go to post Guillaume Rongier · Jul 3, 2023 SELECT COUNT(*) from Ens.MessageHeader WHERE SourceConfigName = 'EPIC_SIU_IN' AND TO_NUMBER(SessionId) = %ID AND %ID >= (SELECT TOP 1 %ID FROM Ens.MessageHeader a WHERE TimeCreated >= '2016-07-27 05:00:00.000' ORDER BY TimeCreated ASC) AND %ID <= (SELECT TOP 1 %ID FROM Ens.MessageHeader a WHERE TimeCreated < '2016-07-28 05:00:00.000' ORDER BY TimeCreated DESC) Just change the date here and see how fast it is. The idea is to use index and the fastest index in MessageHeader is %ID.
go to post Guillaume Rongier · Jul 3, 2023 🎉, great news ! I join Sergei on latest tag in addition of latest-cd and latest-em.
go to post Guillaume Rongier · Jun 30, 2023 What I have done on one project, is to copy past the code and use it. I haven't used ZPM. Here is an example : https://github.com/grongierisc/RestToDicom
go to post Guillaume Rongier · Jun 29, 2023 If you want mock ObjectScript classes use this : https://github.com/GendronAC/InterSystems-UnitTest-Mocking I had used it, it work quiet well.
go to post Guillaume Rongier · Jun 29, 2023 If you want mock ObjectScript classes use this : https://github.com/GendronAC/InterSystems-UnitTest-Mocking I had used it, it work quiet well.
go to post Guillaume Rongier · Jun 26, 2023 Hi, Have a look at this class (https://github.com/grongierisc/RestToDicom/blob/master/src/RestToDicom/P...). It's from ENSDEMO, it's kind of a mock that fake an DICOM modality and create a fake response for ECHO and C-FIND-RQ. It can be a good start.
go to post Guillaume Rongier · Jun 26, 2023 Hi @Evgeny Shvarov Yes without any issue, you can mix it as much as you want it. You have a full example here : https://github.com/grongierisc/RestToDicom/
go to post Guillaume Rongier · Jun 13, 2023 What a neat article, very useful and with a lot of details. Thanks :)
go to post Guillaume Rongier · Jun 12, 2023 Hi, Do you know that every component in a production has a OnInit() method that is called when the component starts? You can use this method to set the value of the parameter. For example: Method OnInit() As %Status { Set ..#Token = $System.Util.GetEnviron("Token") Quit $$$OK } May be it's more elegant to do so than update the parameter in the Production file. FYI, that what i do in IOP (Interoperability On Python) to set the value of the parameter of any component in a production. def on_init(self): self.my_param = os.environ.get("MY_PARAM", "default_value")
go to post Guillaume Rongier · Jun 7, 2023 Good idea, it environment variable is not found switch to settings value.
go to post Guillaume Rongier · Jun 5, 2023 Hi Evegny, You can do it in the following way: * in the production settings, with DefaultSettings docs here * but default settings is a kind of replacement for environment variables * you can to it in the code : * ObjectScript: $system.Util.GetEnviron("MY_ENV_VAR") * Python: os.environ['MY_ENV_VAR'] * you can use iop (interoperabilty on python) to import production in python way : settings.py import os PRODUCTIONS = [ { 'shvarov.i14y.Production': { "Item": [ { "@Name": "shvarov.i14y.ChatOperation", "@ClassName": "Telegram.BusinessOperation", "@Category": "Reddit", "@PoolSize": "1", "@Enabled": "true", "@Foreground": "false", "@Comment": "", "@LogTraceEvents": "false", "@Schedule": "", "Setting": [ { "@Target": "Adapter", "@Name": "SSLConfig", "#text": "default" }, { "@Target": "Adapter", "@Name": "Token", "#text": os.environ['TELEGRAM_TOKEN'] } ] } ] } } ] and then in the terminal: $ iop -M settings.py More information about iop and settings.py here
go to post Guillaume Rongier · Jun 4, 2023 Hi Evgeny, Thanks for your feedback. The -x option is for status, because -s is already used to start a production. The -e option is for export, and the export can be imported with the -m option. -m option stand for migrate, because I took the inspiration from the migrate command form django. I hope it's more clear now. But if you think, -i for import and -e for export is more clear, I can change it. Same for -s and -x. What do you think can be short for status?
go to post Guillaume Rongier · Apr 19, 2023 Awesome, how do you position this tool compared to irissqlcli if it had a web version?
go to post Guillaume Rongier · Apr 18, 2023 I love it <3, for the more you are using the community driver DB-API :)