go to post Sylvain Guilbaud · Oct 13 congratulations to all participants for all these great contents 😀
go to post Sylvain Guilbaud · Sep 11 Hi @Jeff Liu very good demo indeeed ! I've tried to play with it locally, but it seems the docker-compose.yml is containing messages.log. Best regards, Sylvain
go to post Sylvain Guilbaud · Aug 20 Thanks, @John Murray — without you, this community would just be a bunch of code and no soul!
go to post Sylvain Guilbaud · Aug 7 Hi Tanguy, happy to hear you've finally fixed it. Again, %ZSTART is not at all mandatory ; as soon as your ENV variables are available in your container you should be able to get it either from $system.Util.GetEnviron(env) or ##class(%SYS.Python).Import("os").getenv(env) With these variables : {"Env": [ "API_CLIENT_SECRET=ea5663885513e5b00df120fa4b4da8e1150398cde9d41ee27b5a8c6f1898dfa63ae711b82bf06b36475b646453a9092f5653895ddd2c3bb067d9a4f562a6b625", "TZ=Europe/Paris", "ISC_DATA_DIRECTORY=/irisdata", "ISC_CPF_MERGE_FILE=/app/merge.cpf", "API_CLIENT_ID=3e726f42daaa06a8", "ISC_PACKAGE_IRISGROUP=irisowner", "ISC_PACKAGE_IRISUSER=irisowner", "ISC_PACKAGE_MGRGROUP=irisowner", "ISC_PACKAGE_MGRUSER=irisowner", "IRISSYS=/home/irisowner/irissys", "TINI_VERSION=v0.19.0", "ISC_PACKAGE_INSTANCENAME=IRIS", "ISC_PACKAGE_INSTALLDIR=/usr/irissys", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/irisowner/bin", "PYTHONPATH=/usr/irissys/mgr/python", "LANG=en_US.UTF-8", "LANGUAGE=en_US.UTF-8", "LC_ALL=en_US.UTF-8" ]} You should get : write $SYSTEM.Util.GetEnviron("API_CLIENT_ID") 3e726f42daaa06a8 write $SYSTEM.Util.GetEnviron("API_CLIENT_SECRET") ea5663885513e5b00df120fa4b4da8e1150398cde9d41ee27b5a8c6f1898dfa63ae711b82bf06b36475b646453a9092f5653895ddd2c3bb067d9a4f562a6b625 write $SYSTEM.Util.GetEnviron("TZ") Europe/Paris write ##class(%SYS.Python).Import("os").getenv("API_CLIENT_ID") 3e726f42daaa06a8 write ##class(%SYS.Python).Import("os").getenv("API_CLIENT_SECRET") ea5663885513e5b00df120fa4b4da8e1150398cde9d41ee27b5a8c6f1898dfa63ae711b82bf06b36475b646453a9092f5653895ddd2c3bb067d9a4f562a6b625 write ##class(%SYS.Python).Import("os").getenv("TZ") Europe/Paris Or, with %ZSTART containing : ROUTINE %ZSTART QUIT ; Prevents direct execution without a label SYSTEM ; Subroutine called at system startup ; Initialize the IRIS application secrets Set ^Secrets("API_CLIENT_ID") = $System.Util.GetEnviron("API_CLIENT_ID") Set ^Secrets("API_CLIENT_ID","LastUpdated") = $ZDATETIME($HOROLOG,3) Set ^Secrets("API_CLIENT_SECRET") = $System.Util.GetEnviron("API_CLIENT_SECRET") Set ^Secrets("API_CLIENT_SECRET","LastUpdated") = $ZDATETIME($HOROLOG,3) QUIT you should obtain : IRISAPP>zw ^Secrets ^Secrets("API_CLIENT_ID")="3e726f42daaa06a8" ^Secrets("API_CLIENT_ID","LastUpdated")="2025-08-07 17:16:19" ^Secrets("API_CLIENT_SECRET")="ea5663885513e5b00df120fa4b4da8e1150398cde9d41ee27b5a8c6f1898dfa63ae711b82bf06b36475b646453a9092f5653895ddd2c3bb067d9a4f562a6b625" ^Secrets("API_CLIENT_SECRET","LastUpdated")="2025-08-07 17:16:19" IRISAPP>d SYSTEM^%ZSTART IRISAPP>zw ^Secrets ^Secrets("API_CLIENT_ID")="3e726f42daaa06a8" ^Secrets("API_CLIENT_ID","LastUpdated")="2025-08-07 17:24:02" ^Secrets("API_CLIENT_SECRET")="ea5663885513e5b00df120fa4b4da8e1150398cde9d41ee27b5a8c6f1898dfa63ae711b82bf06b36475b646453a9092f5653895ddd2c3bb067d9a4f562a6b625" ^Secrets("API_CLIENT_SECRET","LastUpdated")="2025-08-07 17:24:02"
go to post Sylvain Guilbaud · Aug 6 Tanguy, using my sample repo, you should be able to see in the ^Secrets global :
go to post Sylvain Guilbaud · Aug 6 Tanguy, you can find a full example of retrieving environment variables from a Business Operation either from a global or from $system.Util.GetEnviron(env) in this repo:https://github.com/SylvainGuilbaud/iris-dev
go to post Sylvain Guilbaud · Aug 6 Hi Tanguy, in your environment variables I can see APIclientid and APIclientsecret but nothing called lifenclientid nor lifenclientsecret. That's probably why you don't get any value.
go to post Sylvain Guilbaud · Aug 6 Hi Tanguy, %ZSTART routine is not mandatory. It's just an example of a way to retrieve and store data at instance startup. If it works you should see the results in System > Globals > View Global Data > ^IRIS.Temp global.If $System.Util.GetEnviron("APIclientid") works, it means that your environment variables are well configured.Could you please share your business operation code ?
go to post Sylvain Guilbaud · Jul 31 Hi @TAZ.R Environment variables set in the Docker container are only visible to processes that inherit the environment. You can rapidly check it from a terminal session : write $system.Util.GetEnviron("TZ") Europe/Paris write ##class(%SYS.Python).Import("os").getenv("TZ") Europe/Paris Or from a simple Business Operation : BusinessOperation env Class operations.env Extends Ens.BusinessOperation { Method env(pRequest As Ens.StringContainer, Output pResponse As Ens.StringContainer) As %Status { set sc=$$$OK Try { set env = pRequest.StringValue set value = $system.Util.GetEnviron(env) set pResponse = ##class(Ens.StringContainer).%New() set pResponse.StringValue = value $$$LOGINFO("Environment: "_env _":"_value) } Catch ex { Set tSC=ex.AsStatus() $$$LOGERROR("ERROR:" _ $system.Status.GetErrorText(tSC)) set sc = tSC } return sc } Method pyenv(pRequest As Ens.StringRequest, Output pResponse As Ens.StringContainer) As %Status { set sc=$$$OK Try { set env = pRequest.StringValue // Use Python to get the environment variable set value = ##class(%SYS.Python).Import("os").getenv(env) // Call the Python method to get the environment variable #; set value = ..getEnv(env) ; Uncomment this line if you want to use the Python method directly set pResponse = ##class(Ens.StringContainer).%New() set pResponse.StringValue = value $$$LOGINFO("Environment: "_env _":"_value) } Catch ex { Set tSC=ex.AsStatus() $$$LOGERROR("ERROR:" _ $system.Status.GetErrorText(tSC)) set sc = tSC } return sc } Method getEnv(env As %String) As %String [ Language = python ] { import os value = os.getenv(env) return value } XData MessageMap { <MapItems> <MapItem MessageType="Ens.StringContainer"> <Method>env</Method> </MapItem> <MapItem MessageType="Ens.StringRequest"> <Method>pyenv</Method> </MapItem> </MapItems> } } You could also store the content in a global at IRIS startup by creating the %ZSTART routine ROUTINE %ZSTART QUIT ; Prevents direct execution without a label SYSTEM ; Subroutine called at system startup Set ^IRIS.Temp("Secrets", $System.Util.GetEnviron("API_CLIENT_ID")) = $System.Util.GetEnviron("API_CLIENT_SECRET") QUIT That way, you can retrieve your secret by executing this code: $Get(^IRIS.Temp("Secrets", "your-client-id"), "") Business Operation secret Class operations.secrets Extends Ens.BusinessOperation { Method secret(pRequest As Ens.StringContainer, Output pResponse As Ens.StringContainer) As %Status { set sc=$$$OK Try { set env = pRequest.StringValue set value = $Get(^IRIS.Temp("Secrets", env), "") set pResponse = ##class(Ens.StringContainer).%New() set pResponse.StringValue = value $$$LOGINFO("Environment: "_env _":"_value) } Catch ex { Set tSC=ex.AsStatus() $$$LOGERROR("ERROR:" _ $system.Status.GetErrorText(tSC)) set sc = tSC } return sc } XData MessageMap { <MapItems> <MapItem MessageType="Ens.StringContainer"> <Method>secret</Method> </MapItem> </MapItems> } }
go to post Sylvain Guilbaud · Jul 17 Thank you @Anastasia Dyubaylo for these guidelines which are always good to remember. I don't know if this is intentional on your part to ensure that your article is properly read by the community, but a typo has crept in: %SYSTEM.JSON isn't an existing class in IRIS. Or is this simply an unverified hallucination, which would prove that you yourself should be applying your recommendations for the proper use of generative AI? I'd go with the first option 😊
go to post Sylvain Guilbaud · Feb 18 You can also use the ":sql" alias to launch the SQL Shell : USER>:sql SQL Command Line Shell ---------------------------------------------------- The command prefix is currently set to: <<nothing>>. Enter <command>, 'q' to quit, '?' for help. [SQL]USER>>select sysdate 2. select sysdate | Expression_1 | | -- | | 2025-02-18 13:39:24 | 1 Rows(s) Affected statement prepare time(s)/globals/cmds/disk: 0.0928s/35,642/155,490/1ms execute time(s)/globals/cmds/disk: 0.0001s/3/408/0ms query class: %sqlcq.USER.cls6 --------------------------------------------------------------------------- [SQL]USER>>quit USER> And just type ":?" to get the list of all the alias : USER>:? :<number> Recall command # <number> :py Do $system.Python.Shell() :mdx Do $system.DeepSee.Shell() :sql Do $system.SQL.Shell() :tsql Do $system.SQL.TSQLShell() :alias Create/display aliases :unalias Remove aliases :history Display command history :clear Clear history buffer :? Display help Ctrl+R Reverse incremental search USER>
go to post Sylvain Guilbaud · Jan 13 You can schedule the following task which removes any file from a directory, based on its age, using Python: Class admin.purge Extends %SYS.Task.Definition { Property Directory As %String(MAXLEN = 2000) [ InitialExpression = "/usr/irissys/mgr/Backup" ]; Property DaysToKeep As %Integer(VALUELIST = ",0,1,2,3,4,5") [ InitialExpression = "1" ]; Method OnTask() As %Status { set sc = $$$OK Try { do ..purge(..Directory,..DaysToKeep) } Catch ex { Set sc=ex.AsStatus() } return sc } ClassMethod purge(path As %String, daysToKeep As %Integer) As %Status [ Language = python ] { import iris import os import time from datetime import datetime, timedelta event = "[TASK PURGE OLD BACKUP FILES]" class FileDeletionError(Exception): """Custom exception for file deletion errors.""" pass def delete_old_files(path, days_limit): limit_date = datetime.now() - timedelta(days=int(days_limit)) for file in os.listdir(path): file_path = os.path.join(path, file) if os.path.isfile(file_path): creation_date = datetime.fromtimestamp(os.path.getctime(file_path)) if creation_date < limit_date: try: os.remove(file_path) iris.cls("%SYS.System").WriteToConsoleLog(f"{event} Deleted: {str(file_path)}") except PermissionError: raise FileDeletionError(f"Permission error: Unable to delete {file}") except FileNotFoundError: raise FileDeletionError(f"File not found: {file}") except Exception as e: raise FileDeletionError(f"Unexpected error while deleting {file}: {str(e)}") try: iris.cls("%SYS.System").WriteToConsoleLog(f"{event} Executing task to delete backup files from directory {path} created more than {str(daysToKeep)} days ago") days_limit = daysToKeep delete_old_files(path, days_limit) except FileDeletionError as e: iris.cls("%SYS.System").WriteToConsoleLog(f"{event} Error during deletion: {str(e)}",0,1) except Exception as e: iris.cls("%SYS.System").WriteToConsoleLog(f"{event} Unexpected error: {str(e)}",0,1) } } 01/13/25-18:21:00:549 (35722) 0 [Utility.Event] [TASK PURGE OLD BACKUP FILES] Executing task to delete backup files from directory /usr/irissys/mgr/Backup created more than 0 days ago 01/13/25-18:21:00:550 (35722) 0 [Utility.Event] [TASK PURGE OLD BACKUP FILES] Deleted: /usr/irissys/mgr/Backup/FullAllDatabases_20250113_009.cbk 01/13/25-18:21:00:550 (35722) 0 [Utility.Event] [TASK PURGE OLD BACKUP FILES] Deleted: /usr/irissys/mgr/Backup/FullAllDatabases_20250113_008.log 01/13/25-18:21:00:598 (35722) 0 [Utility.Event] [TASK PURGE OLD BACKUP FILES] Deleted: /usr/irissys/mgr/Backup/FullAllDatabases_20250113_008.cbk 01/13/25-18:21:00:598 (35722) 0 [Utility.Event] [TASK PURGE OLD BACKUP FILES] Deleted: /usr/irissys/mgr/Backup/FullAllDatabases_20250113_009.log
go to post Sylvain Guilbaud · Jan 13 You can schedule the following task which removes any file from a directory, based on its age, using Python: Class admin.purge Extends %SYS.Task.Definition { Property Directory As %String(MAXLEN = 2000) [ InitialExpression = "/usr/irissys/mgr/Backup" ]; Property DaysToKeep As %Integer(VALUELIST = ",0,1,2,3,4,5") [ InitialExpression = "1" ]; Method OnTask() As %Status { set sc = $$$OK Try { do ..purge(..Directory,..DaysToKeep) } Catch ex { Set sc=ex.AsStatus() } return sc } ClassMethod purge(path As %String, daysToKeep As %Integer) As %Status [ Language = python ] { import iris import os import time from datetime import datetime, timedelta event = "[TASK PURGE OLD BACKUP FILES]" class FileDeletionError(Exception): """Custom exception for file deletion errors.""" pass def delete_old_files(path, days_limit): limit_date = datetime.now() - timedelta(days=int(days_limit)) for file in os.listdir(path): file_path = os.path.join(path, file) if os.path.isfile(file_path): creation_date = datetime.fromtimestamp(os.path.getctime(file_path)) if creation_date < limit_date: try: os.remove(file_path) iris.cls("%SYS.System").WriteToConsoleLog(f"{event} Deleted: {str(file_path)}") except PermissionError: raise FileDeletionError(f"Permission error: Unable to delete {file}") except FileNotFoundError: raise FileDeletionError(f"File not found: {file}") except Exception as e: raise FileDeletionError(f"Unexpected error while deleting {file}: {str(e)}") try: iris.cls("%SYS.System").WriteToConsoleLog(f"{event} Executing task to delete backup files from directory {path} created more than {str(daysToKeep)} days ago") days_limit = daysToKeep delete_old_files(path, days_limit) except FileDeletionError as e: iris.cls("%SYS.System").WriteToConsoleLog(f"{event} Error during deletion: {str(e)}",0,1) except Exception as e: iris.cls("%SYS.System").WriteToConsoleLog(f"{event} Unexpected error: {str(e)}",0,1) } } The task logs its actions in messages.log : 01/13/25-18:21:00:549 (35722) 0 [Utility.Event] [TASK PURGE OLD BACKUP FILES] Executing task to delete backup files from directory /usr/irissys/mgr/Backup created more than 0 days ago 01/13/25-18:21:00:550 (35722) 0 [Utility.Event] [TASK PURGE OLD BACKUP FILES] Deleted: /usr/irissys/mgr/Backup/FullAllDatabases_20250113_009.cbk 01/13/25-18:21:00:550 (35722) 0 [Utility.Event] [TASK PURGE OLD BACKUP FILES] Deleted: /usr/irissys/mgr/Backup/FullAllDatabases_20250113_008.log 01/13/25-18:21:00:598 (35722) 0 [Utility.Event] [TASK PURGE OLD BACKUP FILES] Deleted: /usr/irissys/mgr/Backup/FullAllDatabases_20250113_008.cbk 01/13/25-18:21:00:598 (35722) 0 [Utility.Event] [TASK PURGE OLD BACKUP FILES] Deleted: /usr/irissys/mgr/Backup/FullAllDatabases_20250113_009.log