go to post Alex Woodhead · Feb 15, 2023 Currently they seem to be stored under:^%sqlcq([Namespace], "SMPQueryHistory", [Username], ...
go to post Alex Woodhead · Feb 15, 2023 This means there is no defined data node at this level, but there are subnodes defined. ( Where $Data == 10 ) Remove the ending bracket ")" in Global Explorer the page will display the subnodes. ie: "^ROUTINE(" To see all first keys in Global Explorer: ^ROUTINE() Or with a comma display all first and second subkeys eg: ^Routine( , ) To see the line count for each Routine: ^ROUTINE( , , 0) $Data helps programmatically test for a global reference: 0 == Global is not defined 1 == Is a data node only 10 == Has no Data, but has subnodes (~pointer) 11 == Both is a data node AND has subnodes If unsure of how many subkeys a global might have, can use $Query to generically walk a sparse structure beginning to end. An example using $Query: A one liner to walk every global node in ROUTINE, looking at each line for the word "test", case insensitive, and print both the global node location and content of the code to the terminal: s a="^ROUTINE",find="test",q=$Q(@a@("")) f{s q=$Q(@q) q:q="" s v=$ZCVT(@q,"L") w:v[find !,q,!,"::",@q} Hope this helps.
go to post Alex Woodhead · Feb 10, 2023 Could we have Community Projects that incubate, for example in the way that the Apache Software Foundation incubates sub-projects.A sub-project acts as structure to decide to Adopt requirements flagged on the "Ideas Portal".They act as a focus of activity that people can join and leave over time.They provide a roadmap of what is intended to deliverThey would define a membership of both 1) Developers 2) Quality Assurance One benefit could be that feedback to a Project might feel less personal, and more about helping individuals enjoy collaborating and networking.
go to post Alex Woodhead · Feb 1, 2023 Hi Jude, SELECT * FROM Test.Pattern where ColumnABC %PATTERN '1"C"1(1"00",1"01",1"77")1"."1N' Explanation: First Character is "C" Second and Third Character is one of "00", "01" or "77" Fourth Character is "." (dot) Fifth Character is numeric (0 -9) If needing the full range between C00 to C77 then: SELECT * FROM Test.Pattern where ColumnABC %PATTERN '1"C"1(1(1"0",1"1",1"2",1"3",1"4",1"5",1"6")1N,1"7"1(1"0",1"1",1"2",1"3",1"4",1"5",1"6",1"7"))1"."1N' First Character is "C" Second and third character are from Option 1 - Second character is "0" to "6" Third character is numeric "0" to "9" Option 2 - Second character is "7" Third character is numeric "0" to "7" Fourth Character is "." (dot) Fifth Character is numeric (0 -9) Some further notes. These patterns also work unmodified in IRIS Object Script so you might have code: ClassMethod ProcessCodeInline(code) As %Boolean { if code?1"C"1(1(1"0",1"1",1"2",1"3",1"4",1"5",1"6")1N,1"7"1(1"0",1"1",1"2",1"3",1"4",1"5",1"6",1"7"))1"."1N { Write !,"Matched code pattern" } else { Write !,"No match of code pattern" } } So same pattern for both your SQL and ObjectScript. The pattern AND IF structure may get a bit visually daunting so you can use indirection operator "@" to move the pattern into a Class Parameter. This also allows you to reuse the same match expression but manage the expression in one place: Parameter codepattern = "1""C""1(1(1""0"",1""1"",1""2"",1""3"",1""4"",1""5"",1""6"")1N,1""7""1(1""0"",1""1"",1""2"",1""3"",1""4"",1""5"",1""6"",1""7""))1"".""1N"; ClassMethod ProcessCode(code) As %Boolean { if code?@..#codepattern { Write !,"Matched code pattern" } else { Write !,"No match of code pattern" } } If you see this double quoted pattern in parameter, code or in a configuration global, it is a clue it is applied in a pattern via indirection by the application. set ^matchme("LabFormat")="1""C""1(1(1""0"",1""1"",1""2"",1""3"",1""4"",1""5"",1""6"")1N,1""7""1(1""0"",1""1"",1""2"",1""3"",1""4"",1""5"",1""6"",1""7""))1"".""1N" if "C01.8"?@^matchme("LabFormat") Write !,"Matched code pattern" Cheers, Alex
go to post Alex Woodhead · Jan 31, 2023 Hi Evgeny, The ompare tool has a Schedule option that happily exports classes, routines, productions, lookup tables, HL7 schemas. It can be configured to traverse multiple namespaces in a single run, generating an export file for each namespace. Wonder if this is useful for your case. A periodic export / snapshot of some components. Cheers, Alex
go to post Alex Woodhead · Jan 30, 2023 Hi Scott, Minimal sub-classing could be like the following: Note: If there is no modification, it simply presents the original unmodified filestream. Class Test.ReframeService Extends EnsLib.HL7.Service.FileService { /// Broken line HL7 seperator replcaement. /// Alternatively could use "\.br\"" Parameter hl7newline = "~"; Method OnProcessInput(pFileStream As %CharacterStream, Output pOutput As %RegisteredObject) As %Status { do ##class(Test.ReframeService).Reframe(pFileStream) Quit ##super(.pFileStream,.pOutput) } ClassMethod Reframe(inStream As %Stream.FileCharacter) { do inStream.Rewind() set outStream=##class(%Stream.TmpCharacter).%New() set buffer="",state=0,step=0,modified=0 for { quit:inStream.AtEnd set char=inStream.Read(1) set uchar=$ZCVT(char,"U") if state=0,char=$C(13) { set state=1,buffer=char,step=1 continue } if "01"[state,char=$C(10) { set state=2,buffer=buffer_char continue } if state=2,uchar=char,char?1A { set state=3,buffer=buffer_char continue } if state=3,uchar=char,char?1A { set state=4,buffer=buffer_char continue } if state=4,(((char?1A)&&(uchar=char))||(char?1N)) { set state=5,buffer=buffer_char continue } if state=5,char="|" { set state=0 do outStream.Write(buffer_char) set buffer="",step=0 continue } if state { set state=0,modified=1 do outStream.Write(..#hl7newline_$E(buffer,step+2,*)_char) set buffer="" continue } if $L(buffer) { do outStream.Write(buffer) set buffer="" } set state=0,step=0 do outStream.Write(char) } do:$L(buffer) outStream.Write(buffer) do inStream.Rewind() if modified { do outStream.Rewind() do inStream.CopyFromAndSave(outStream) do inStream.Rewind() } } }
go to post Alex Woodhead · Jan 29, 2023 Hi Scott, Have created an example state machine to walk three character segment names "AAA" or "AAN". You can change the hl7 seperator via the parameter as annotated. Class Test.ReframeHL7 [ Abstract ] { /// Broken line HL7 seperator replcaement. /// Alternatively could use "\.br\"" Parameter hl7newline = "~"; ClassMethod Process(inStream As %Stream.FileCharacter, outStream As %Stream.TmpCharacter) { do inStream.Rewind() set:'$IsObject(outStream) outStream=##class(%Stream.TmpCharacter).%New() set buffer="",state=0,step=0 for { quit:inStream.AtEnd set char=inStream.Read(1) set uchar=$ZCVT(char,"U") if state=0,char=$C(13) { set state=1,buffer=char,step=1 continue } if "01"[state,char=$C(10) { set state=2,buffer=buffer_char continue } if state=2,uchar=char,char?1A { set state=3,buffer=buffer_char continue } if state=3,uchar=char,char?1A { set state=4,buffer=buffer_char continue } if state=4,(((char?1A)&&(uchar=char))||(char?1N)) { set state=5,buffer=buffer_char continue } if state=5,char="|" { set state=0 do outStream.Write(buffer_char) set buffer="",step=0 continue } if state { set state=0 do outStream.Write(..#hl7newline_$E(buffer,step+2,*)_char) set buffer="" continue } if $L(buffer) { do outStream.Write(buffer) set buffer="" } set state=0,step=0 do outStream.Write(char) } do:$L(buffer) outStream.Write(buffer) } /// set tSC=##class(Test.ReframeHL7).Test(inFilename,outFilename) ClassMethod Test(inFilename, outFilename) As %Status { #dim inFile as %IO.FileStream=##class(%IO.FileStream).%New() set tSC=inFile.Open(inFilename,"RS") quit:$$$ISERR(tSC) tSC #dim outFile as %IO.FileStream=##class(%IO.FileStream).%New() set tSC=outFile.Open(outFilename,"NWS") quit:$$$ISERR(tSC) tSC Do ..Process(inFile,outFile) Do outFile.Flush() Do outFile.Close() Do inFile.Close() quit $$$OK } } Cheers, Alex
go to post Alex Woodhead · Jan 28, 2023 Hi Martin, If you have sudo access you could switch the user for the iris session command, then run command as before For example: cd /tmpsudo -u irisowner iris session irisinstancename OR even: cd /tmpsudo -u irisusr iris session irisinstancename Hope this helps. Cheers, Alex
go to post Alex Woodhead · Jan 28, 2023 Hi Ravi, Variables that start with "%" for example %request, %session and %request may be in scope from adapter interactions. Otherwise the Adaptor helpfully adds a lot to the Stream Attributes The following may give ideas for Headers available: Class Test.HTTP.Service Extends EnsLib.HTTP.Service { Parameter ADAPTER = "EnsLib.HTTP.InboundAdapter"; Method OnProcessInput(pInput As %Stream.Object, Output pOutput As %Stream.Object) As %Status { #dim %request as %CSP.Request set pOutput=##class(%Stream.TmpCharacter).%New() do pOutput.Write("<p>AUTH:"_%session.HttpAuthorization_"</p>") do pOutput.Write("</hr>") set attribute="" for { set attribute=$Order(pInput.Attributes(attribute),1,data) quit:attribute="" do pOutput.Write("attribute:"_attribute_" was "_data_"</br>") } do pOutput.Write("<hr/>") set attribute="" for { set attribute=$O(%request.CgiEnvs(attribute),1,data) quit:attribute="" do pOutput.Write("attribute:"_attribute_" was "_data_"</br>") } do pOutput.Write("</hr>") do pOutput.Write("<p>Username:"_$USERNAME_"</p>") do pOutput.Rewind() Quit $$$OK } } Accessed with Enable-Standard-Resuests=True. With request sent to CSP Application with password authentication. http://localhost:[portnumber]/csp/healthshare/[namespace]/Test.HTTP.Serv... Where "TestHTTP" was the Production Item name. Couldn't see the AUTH Header BUT can see the resulting validated Username in $USERNAME special variable. Hope this gives some ideas. Cheers, Alex
go to post Alex Woodhead · Jan 25, 2023 Yes these 3 tools are all for test automation and shared to Open Exchange: 1. For stressing Routing Rules: https://openexchange.intersystems.com/package/UnitTest-RuleSetEach UnitTest holds a Common Base message.With each UnitTest method, modify the base message, send to router, and ASSERT the routing reason was as expected.Had been useful on a project where there were over 100 different variations on a message to validate for correct routing.Adding an additional routing variation, was quick (2 minutes) to automatically retest had not broken previous behavior. Would have taken a day of manual testing otherwise. 2. For validating DTL output of HL7 v2 originally.https://openexchange.intersystems.com/package/UnitTest-DTL-HL7For productivity:* Scans namespace for existing DTL filtered by give a search path.* Generates corresponding DTL UnitTest classes for each source DTL.* Generates a reusable compare method. Allows to comment out asserts to ignore volatile content like Generated Identifiers and Dates. Has some HL7 oriented UnitTest Assert extensions for reuse.* Provides a method for automatically attaching an existing HL7 message, to a generated UnitTest class as base message.Isolates / focuses testing of DTL behavior from rest of end-to-end testing. 3. BulkProfile was a new tool variation for Scott's requirement for comparing routing of same source messages between different versions of software.https://openexchange.intersystems.com/package/BulkProfile-HL7RoutingRulesA confirmation that migration of platform version did not affect existing routing behavior. Zero code expectation.
go to post Alex Woodhead · Jan 17, 2023 Hi Virat,In the past it had been really useful to become proficient with managing the operating system (eg: RedHat) to achieve effective Database provisioning and management. For example: Shell scripting, snapshots, filesystem and process permissions (selinux), monitoring (process, io, errors), migration and upgrade processes, and to use an automation framework like Puppet or Ansible.However times and skill needs are changing to a cloud first approach.For future proofing skills would now advocate for learning IRIS on Docker, Kubernertes and gaining proficiency with one or more cloud provider for service access control and provisioning.Python scripting in addition to shell scripts. ie: IRIS embedded python capabilities.Be comfortable with Apache webserver configuration, loadbalancing, and management. Have some ability for Firewalls, DNS and TCP connectivity testing / tracing. Some links:https://www.docker.com/https://kubernetes.io/https://prometheus.io/ Suggest follow Murray Oldfield articles here on the community. https://community.intersystems.com/post/yaspe-yet-another-system-perform... Hope this helps.
go to post Alex Woodhead · Dec 28, 2022 Hi João, I took your example for a drive. The source files and code are very useful to investigate the issue. The sample document basicTemplate.jxml seemed to be incompatible with the schema. Which maybe why the schema validation step is not happy. Locally the reports schema was imported to package "jr" and components imported to package "c". Then I needed to make the following enhancements to some of the generated classes. Modified: jr.componentElement// Commented out// Property component As jr.componentType(XMLNAME = "component", XMLREF = 1) [ Required ];// Needed to manually add:Property list As c.list;Property barbecue As c.barbecue;Property Codabar As c.Codabar;Property Code128 As c.Barcode4JCode128;Property EAN128 As c.EAN128;Property DataMatrix As c.DataMatrix;Property Code39 As c.Code39;Property Interleaved2Of5 As c.Interleaved2Of5;Property UPCA As c.UPCA;Property UPCE As c.EAN13;Property EAN13 As c.EAN13;Property EAN8 As c.EAN8;Property RoyalMailCustomer As c.Barcode4JFourState;Property USPSIntelligentMail As c.Barcode4JFourState;Property POSTNET As c.POSTNET;Property PDF417 As c.PDF417;Property QRCode As c.QRCode;Property map As c.map;Property sort As c.sort;Property table As c.table;Property spiderChart As c.spiderChart;Property iconLabel As c.iconLabel; Class: c.tableNeeded to manually add:Property datasetRun As jr.datasetRun; Class: c.columnNeeded to manually add:Property property As %String(MAXLEN = 220); Class: c.TableCellNeeded to manually add:Property box as jr.box;Property staticText As jr.staticText;Property property As %String(MAXLEN = 220); My approach to figure this out was to respond to correlation errors like: ERROR #6237: Unexpected tag in XML input: property (ending at line 64 character 71).Correlate status | 1 Where there was an unexpected element called "property" that didn't have a corresponding property in the generated class definition. For componentElement I had replaced the tag "component" with distinct properties for each actual element name that would be encountered. I think if the sample document had used element name "component" instead of "jr.table" the XMLReader would have been able to determine what was needed. It seems to be a workable solution to adjust the generated classes slightly to fit the sample provided. Hope this helps. Cheers, Alex
go to post Alex Woodhead · Dec 15, 2022 Hi Anna. I have written a function to work for MAC, INT and INT generated from Classes, based on stack comment earlier. For user to test and confirm suitable for purpose :) Usage from a class method or routine. Returns the line number. Arguments optional to capture other context. // how to call set linenumber= ##class(DC.LineNumber).SrcLineNumberFromStack(.routine,.label,.offset,.src) // debug write !,"routine: ",routine write !,"label: ",label write !,"offset: ",offset write !,"linenumber: ",linenumber write !,"src:",src Implementation: Class DC.LineNumber [ Abstract ] { ClassMethod SrcLineNumber(routine As %String, label As %String, offset As %Integer = 0, Output src) As %Integer { if '$Data(^rMAC(routine)),$Data(^ROUTINE(routine)) { // INT code only possibly generated set globref="^ROUTINE" } else { set globref="^rMAC" } set labelLen=$Length(label) set line=0 for { // Try use rMAC if available as ROUTINE may have subsequent imported includes changing original line offsets set line=$Order(@globref@(routine,0,line),1,data) // reached the end of the routine quit:line="" set matchTest=$Extract(data,1,labelLen) // discard non-matching lines continue:$Extract(data,1,labelLen)'=label // Looking for an exact match eg: Myline // skip linelabels with prefix same as exact line label // eg: MyLableBefore, MyLableAfter // ie comprised by following alphanumeric character continue:$Extract(data,labelLen+1)?1AN // Found the line so quit here quit:matchTest=label } set line=line+offset set src=$Select(line>0:$Get(@globref@(routine,0,line)),1:"") quit line } /// Unpack current location ClassMethod SrcLineNumberFromStack(Output routine As %String, Output label As %Integer, Output offset As %Integer, Output src As %String) As %Integer { quit:$Stack=1 0 set place=$Stack(($Stack)-1,"PLACE") set routine=$Piece($Piece(place,"^",2)," ") set offset=+$P($Piece(place,"^"),"+",2) set label=$P($Piece(place,"^"),"+") set line=(..SrcLineNumber(routine,label,offset,.src)) quit line }
go to post Alex Woodhead · Dec 6, 2022 To clear a range of user tasks had used similar to: for i=14:1:17 {do ##class(%SYS.Task).%DeleteId(i)}
go to post Alex Woodhead · Nov 11, 2022 Hi @Joel Solon Yes the $Sortbegin on the ^*D global was simply to demonstrate different possible outcomes. You are right the solution only requires to discard the index ( ^*I) updates.I will update my above reply.
go to post Alex Woodhead · Oct 26, 2022 Hi Michael, Maybe you are reproducing some application migration issue and are setting up some test data. Purely as a "diagnostic" it seems possible to use the $SORTBEGIN and $SORTEND ObjectScript functions to effect this potentially without changing existing code. Basically you can ringfence specific globals (normally for performance rationale) and transiently store the global writes in buffer. Then at end of "experiment", you can decide what to commit. ie: Commit the data writes but throw away the index writes. Hope this helps. Class TEST.Claim Extends %Persistent{ Property PropA As %String; Property PropB As %String; Index ixdPropA On PropA; Index ixdPropB On PropB; /// Do ##class(TEST.Claim).TestWithIndex()ClassMethod TestWithIndex(){K ^TEST.ClaimD,^TEST.ClaimIfor i=1:1:10 {set obj=..%New()set obj.PropA="PropA V"_iset obj.PropB="PropB V"_ido obj.%Save()}zw ^TEST.ClaimD,^TEST.ClaimI}/// Do ##class(TEST.Claim).TestWithoutIndex()ClassMethod TestWithoutIndex(){K ^TEST.ClaimD,^TEST.ClaimIi $SORTBEGIN(^TEST.ClaimD) // Buffer the writesi $SORTBEGIN(^TEST.ClaimI) // Buffer the writesfor i=1:1:10 {set obj=..%New()set obj.PropA="PropA V"_iset obj.PropB="PropB V"_ido obj.%Save()}i $SORTEND(^TEST.ClaimD,1) // Keep datai $SORTEND(^TEST.ClaimI,0) // Throw Indexes awayzw ^TEST.ClaimD,^TEST.ClaimI} Storage Default{<Data name="ClaimDefaultData"><Value name="1"><Value>%%CLASSNAME</Value></Value><Value name="2"><Value>PropA</Value></Value><Value name="3"><Value>PropB</Value></Value></Data><DataLocation>^TEST.ClaimD</DataLocation><DefaultData>ClaimDefaultData</DefaultData><IdLocation>^TEST.ClaimD</IdLocation><IndexLocation>^TEST.ClaimI</IndexLocation><StreamLocation>^TEST.ClaimS</StreamLocation><Type>%Storage.Persistent</Type>} } Kind regards, Alex Update: For clarification the $Sortbegin on the ^*D global was simply to demonstrate different possible outcomes. The actual solution only required to discard the index ( ^*I) updates.
go to post Alex Woodhead · Oct 11, 2022 The Learning is never complete. There are many ways to achieve functionality, so always be open to recognizing new patterns. Always listen, there are things we don't know yet. Bugfixing is a great way to start. This ensures contact with different programming styles and patterns. Don't just cut-n-paste off the google. Make the time to understand how it works. Coding with IRIS is actually useful in that it gets you to work on many areas as a single developer: webpages, application logic, database stored procedures, data schemas and integration. This cuts right across traditional developer silos Application tier vs Database adminsitration vs Testing vs Integration. Grow a Team mindset. Have the patience to try and work it out yourself. Know when to ask for help. Help others to help you by formulating better questions. Sometimes by getting to the right question you will already see the answer. Work on something you have a passion for.
go to post Alex Woodhead · Oct 10, 2022 Hi Fedor, The type DataObj.Services is a ListOfDataTypes not a primitive List. // create delimited stringset str="A_B_C"// conver to listset tempList = $ListFromString(str, "_")// debug output list primitivezw tempListtempList=$lb("A","B","C") // create stand alone List Of DataTypesset o=##class(%Library.ListOfDataTypes).%New()// add primitive list to object ListOfDataTypesset tSC=o.InsertList(tempList)// Itterate on ListOfDataTypes collectionset k=""for {set item=o.GetNext(.k) quit:k="" write !,item} ABC ... So anticipate using "GetNext" method on the collection is the way to go.
go to post Alex Woodhead · Oct 9, 2022 Hi Evgeny, You could consider using the Python "traceback" module. For example: Class TEST.TraceBack [ Abstract ] { ClassMethod GetErrorTrace() As %String [ Language = python ] { import traceback errStr="" try: x = 7/0 except: errStr=traceback.format_exc() return errStr } } Demonstration: USER>w ##class(TEST.TraceBack).GetErrorTrace() Traceback (most recent call last): File "TraceBack", line 6, in GetErrorTrace ZeroDivisionError: division by zero Kind regards, Alex
go to post Alex Woodhead · Oct 9, 2022 Hi Prashanth, It is possible to view how this is stored. Consider the following class definition. After compiling its default Data Storage location is global: ^TEST.PropertyLenD Class TEST.PropertyLen Extends %Persistent { Property Abc As %String(MAXLEN = ""); Property Def As %String(MAXLEN = ""); Storage Default { <Data name="PropertyLenDefaultData"> <Value name="1"> <Value>%%CLASSNAME</Value> </Value> <Value name="2"> <Value>Abc</Value> </Value> <Value name="3"> <Value>Def</Value> </Value> </Data> <DataLocation>^TEST.PropertyLenD</DataLocation> <DefaultData>PropertyLenDefaultData</DefaultData> <IdLocation>^TEST.PropertyLenD</IdLocation> <IndexLocation>^TEST.PropertyLenI</IndexLocation> <StreamLocation>^TEST.PropertyLenS</StreamLocation> <Type>%Storage.Persistent</Type> } } Creating a record: USER>s o=##class(TEST.PropertyLen).%New() USER>s o.Abc="Hello" USER>s o.Def="World!" USER>d o.%Save() USER>w !,"RowId is ",o.%Id() RowId is 1 Then viewing the record zw ^TEST.PropertyLenD(1) ^TEST.PropertyLenD(1)=$lb("","Hello","World!") USER> So by default there is a single global data node made from a list of all property values. This can also can be viewed in System Management Portal -> System Explorer -> Globals: Kind regards, Alex