Julius Kavay · Jun 23, 2024 go to post
Class DC.BigJSON Extends %RegisteredObject
{

ClassMethod Test(filename)
{
	if ..SaveToFile(..MakeJSON(), filename) {
		write "Save OK",!
		write "Size ",##class(%File).GetFileSize(filename),!
		
		set input=##class(%File).%New(filename)
		set sts=input.Open("RS")
		if sts {
			set json={}.%FromJSON(input)
			set iter=json.%GetIterator()
			while iter.%GetNext(.key, .val) {
				write "key=",key," size=",$l(val)," data=",$e(val,1,10)_"...",!
			}
		} else  { write $system.Status.GetOneErrorText(sts),! }
	}
}

ClassMethod MakeJSON()
{
	set obj={}
	set obj.text1=$tr($j("",3600000)," ","a")
	set obj.text2=$tr($j("",3600000)," ","b")
	set obj.text3=$tr($j("",3600000)," ","c")
	quit obj
}

ClassMethod SaveToFile(obj, filename)
{
	set file=##class(%File).%New(filename)
	set sts=file.Open("wnu")
	if sts {
		do obj.%ToJSON(file)
		do file.Rewind()
		use file.Name 
		do file.OutputToDevice()
		do file.Close()
		quit 1
	} else { quit sts }
}

}

The size shouldn't be a problem


USER>do ##class(DC.BigJSON).Test("/tmp/test1.txt")
Save OK
Size 10800034
key=text1 size=3600000 data=aaaaaaaaaa...
key=text2 size=3600000 data=bbbbbbbbbb...
key=text3 size=3600000 data=cccccccccc...

Julius Kavay · Jun 22, 2024 go to post

The first part (Base 64 encoding is not able to encode ... unicode (2 byte) characters)  is correct. The second part (data-->utf8-->base64 and base64-->utf8-->data) is correct only if there is an agreement beetwen the sender and receiver about the double-encoding (utf8+base64).

If I was told, I get a base64 encoded file then I expect a file which is only base64 encoded and not a mix of several encodings including base64. A simple way to encode your document could be something like this:

ClassMethod Encode(infile, outfile)
{
    // file-binary reads bytes and not characters
    set str = ##class(%Stream.FileBinary).%New()
    set str.Filename = infile
    set len = 24000 // len #3 must be 0 !
    set nonl = 1    // no-newline: do not insert CR+LF
    do str.Rewind()

    open outfile:"nwu":0
    if $test {
        use outfile
        while 'str.AtEnd { write $system.Encryption.Base64Encode(str.Read(len),nonl) }
        close outfile
    }
    quit $test
}
Julius Kavay · May 30, 2024 go to post

Take a look at the classes $system.Security and $system.License, whichever better fulfills your needs

Julius Kavay · May 29, 2024 go to post

Sadly, I haven't a shorher solution than yours but another one (with the same size)

ClassMethod Count(s)
{
	q $l($tr(s,")]}D>"_s,1E4))
}
Julius Kavay · May 28, 2024 go to post

It seems the task definition lacks the preciseness and you have the power of observation. What about this?

ClassMethod Count(s)
{
	q $l($tr(s,")]}>","DDDD"),"D")-1
}
Julius Kavay · May 28, 2024 go to post

In the past we got challenges with a better definition... ;-((  ☹


In the original post, I'm pretty sure, there were
")s)).:D :~) ;~D :) xD ))" instead of
")s).:D :~) ;~D :) xD"

and
"(smiley) ))" instead of
"(smiley)"

Nevertheless,

"Sadness >:( :[ :{ :("  ---> you say: 0
         >                     I say: 1 (0 Eyses, 0 noses 1 mouth)
and there is no rule about interspace requirements

My quick solution:

ClassMethod Count(s)
{
	while $locate(s,"[:;8B=]?[co\^~-]?[\)\]\}D>]",$g(i),i),$i(n){} q +$g(n)
}

/// If one accepts "" as 0 then change +$g(n) to $g(n)

/// I would like a solution as (see the + char in regex)
/// but then the rules should be changed
///
ClassMethod Count(s)
{
	while $locate(s,"[:;8B=]?[co\^~-]?[\)\]\}D>]+",$g(i),i),$i(n){} q +$g(n)
}
Julius Kavay · May 15, 2024 go to post

According to your definition, "A number is Esthetic if ... between every pair of its adjacent digits ...", the test example

test.assert_equals(esthetic(1), [2, 3, 4, 5, 6, 7, 8, 9, 10])

is wrong, because the number one (1) converted in whatever number base is always 1. Looking on that 1 I do NOT see any ADJACENT digit(s), except you define that a single digit is always an esthetic number (but I do not see such a definition).

justmy2cents

Julius Kavay · May 13, 2024 go to post

If your system does not support JSON (i.e. pre 2016.2?) then give this "dirty trick" a try:

- add a zero-width-space character to your numbers
- create the output stream
- remove the zero-width-space characters

Instead of the zero-width-space you can use any other character too, which does not appear in your data (binary data should be base64 encoded).

ClassMethod WithQuotes()
{
	set zwsp = $c(8203) // zero-width-space
	set obj = ##class(%ZEN.proxyObject).%New()
	
	set obj.ID = 1234_zwsp
	set obj.Number=123.45_zwsp
	
	if ##class(%ZEN.Auxiliary.jsonArrayProvider).%WriteJSONStreamFromObject(.tmp,obj) {
  		set json=##class(%Stream.TmpBinary).%New()
		do tmp.Rewind()
		while 'tmp.AtEnd { do json.Write($tr(tmp.Read(32000),zwsp)) }
	}
	
	do json.Rewind()
	write json.Read(json.Size)
}
Julius Kavay · May 2, 2024 go to post

Can you show us your iterator so we can tell you what's wrong with it. Or should we try to guess your code?

Julius Kavay · Apr 26, 2024 go to post

The one way is to use %XML.TextReader

ClassMethod Reader(str, pth, ByRef val)
{
	kill val
	set val=0
	// Adjust the method name below to your needs
	// ParseString(), ParseStream(), ParseFile() 
	if ##class(%XML.TextReader).ParseString(str,.rdr) {
		while rdr.Read() {
			if rdr.NodeType="chars",rdr.Path=pth set val($i(val))=rdr.Value
		}
		quit 1
	}
	quit 0
}

set str="<?xml version=""1.0"" encoding=""UTF-8""?><Input><data><Ids><Id><Type>A</Type><Value>123</Value></Id><Id><Type>B</Type><Value>456</Value></Id></Ids></data></Input>"
write ##class(yourClass).Reader(str,"/Input/data/Ids/Id/Value",.val) --> 1
zw val -->
val=2
val(1)=123
val(2)=456

The other way is to use %XML.Reader() and correlate to a class which describes your XML structure

Julius Kavay · Apr 18, 2024 go to post

I can't help with "how to transfer a file to Azure" (I do not use Azure) but before you transfer an IRIS.dat (or an Cache.dat)  you should DISMOUNT the corresponding database. Transferring a mounted database is like tire changing in a moving car - it's possible but dangerous.

Julius Kavay · Apr 18, 2024 go to post

In the above code just insert a Lock and Unlock, and the problem is solved

  //Buid table if it doesn't exist
  
  lock +^LockOutOthers // or any other name you wish
                       // No timeout! At this point everybody has to wait
                       // as long as the current job does a table check/update
  &SQL(
  ...
  ...
  }
  
  lock -^LockOutOthers  // let the others in
  
  Quit outVisitID
  
Julius Kavay · Apr 15, 2024 go to post

As you wrote,  %XML.TextReader is used to read arbtrary XML documents. "A text where in the middle a little bit xml-structure sits" isn't XML!

Maybe there is a Pyhton library for extracting XML from a text. If not, probably you have to read char-after-char, count each "<" (+1) and ">" (-1) and if the counter is 0 then between the first "<"  and the last ">" probably you have a correct XML structure. Oh, and don't forget for <![CDATA[...]]> sequences, which makes the reading more challenging.

Julius Kavay · Apr 15, 2024 go to post

According to your screenshot, you have 22 licenses, of which 21 are available.  On the left side of the control panel, there should be a node named Process. Click on it, and you see all the current active processes including the user name, if the login process make use of the License.Login() (as far as I remember)

Julius Kavay · Apr 15, 2024 go to post

The above screenshot is from your old Cache (5.x.x)!

You get a "License limit exceeded" error if
- all the licenses are in use
- there is no license set
- possibly when licenses are expired (I'm not sure)

Julius Kavay · Apr 15, 2024 go to post

Your subject (compilation error iris terminal class not found) and the screenshot
- workinhg on Class: Class TESTsAMTraining.NewClassDemo1
- calling a class: do ##class(MyCos.COSTest).Main
- and the error message: <CLASS DOES NOT EXISTS>

do not match!

I do not see anything about a "compilation error" on the other hand, there is an error saying, a class do not exists.

So please help us to help you and try to give us correct data about your problem.
Thank you.

Julius Kavay · Apr 13, 2024 go to post

Just came to my mind
- Cache-5.0.x is likely to be 32 Bit version, Win-11 is 64 Bit (only) (in case, the application uses some .dll, .ocx, etc.)
- unlikely that you use it, but as a hint, LAT is not supported anymore
- user database is now provided by ISC. In case your application maintains its own users, you can still use your own user database, but the login process will require some "adaption"

Julius Kavay · Apr 13, 2024 go to post

As a first step, I would contact your ISC Sales because Cache-5.0.x licenses neither work with (the latest) Cache nor with IRIS. Second, there was a lot of change between Cache-5.0.x and recent Cache/IRIS versions, so I would check to see if there are any problems to expect. A customer of mine "upgraded" fom Cache-5.0.21 to IRIS  some four years ago...

Julius Kavay · Apr 12, 2024 go to post

Just for the case, you are lost in the working memory space and desperately searching the spot(s) in your programm where a specific object is once again referenced, here a small handy method which could help you

/// find all variables which contain a given object(reference)
/// 
/// I: the OREF you looking for
/// 
/// O: "" if the spool-device can't be opened
///    [] if no variables contain the given OREF
///    [var1, var2, ... varN] an array of variable names (incl. subscripted and orefs)
///    
ClassMethod FindObject(obj)
{
	set res=[]
	if $d(%)#10,%=obj do res.%Push("%")
	new % set %=obj kill obj
	
	lock +^SPOOL("nextID")			// adapt this lines
	open 2:($o(^SPOOL(""),-1)+1):1	// to your method of
	lock -^SPOOL("nextID")			// creating new spool IDs
	
	if $t {
		use 2
		set spl=$zb
		do $system.OBJ.ShowReferences(.%,1)
		
		for i=1:1:$za-1 {
			set x=$p($zstrip(^SPOOL(spl,i),"<=>w",$c(13,10))," ",3)
			do:x]"%.~" res.%Push(x)
		}
		close 2
		kill ^SPOOL(spl)
		
	} else { set res="" }
	
	quit res
}

Example

USER>kill
USER>set pers=##class(DC.Person).%OpenId(1)
USER>set temp=pers, zz(3)=temp
USER>write ##class(DC.Help).FindObject(pers).%ToJSON()
["pers","temp","zz(3)"]

Julius Kavay · Apr 11, 2024 go to post

I'm not sure wha you want to achive, so I ask a puzzling question: do you want to create dungling object? "I want to remove the object from memory even if it is still referenced in memory", as I understand, would free the memory used by an object but let the object referenc(es) intact, so the reference now would point into nirvana. Is that what you want to do? Why? Can you a little bit elaborate, what is your target or the background respectively?

Julius Kavay · Apr 11, 2024 go to post

The other way is, to put the classname into a variable and

set var = "MyPackage.MyClass"
do $classmethod(var,"Main")
set x=$classmethod(var,"Othermethod",params)

// or, if you have an instance
set obj=##class(MyClass).%New()
do $method(obj,"Main")
set x=$method(obj,"Othermethod",params)
Julius Kavay · Apr 11, 2024 go to post

If your class is instantiable (i.e. not an abstarct class) then

set obj = ##class(MyPackage.MyClass).%New()
do obj.Main()
set x=obj.OtherMethod(parameter)
Julius Kavay · Apr 10, 2024 go to post

It seems, nobody likes the execute command... once celebrated, now frowned upon

set x="11,22,33,""Done"""
xecute "for i="_x_" write i,!"
11
22
33
Done

😏

Julius Kavay · Apr 10, 2024 go to post

I have a limited embedded python experience but if you can manage to get a (COS)%List from python then the solution is:

set $lb(val1, val2, val2, ...) = ConvertPythonTupelToCOSList(pythonfunction(param))
set $lb(val1, val2, val2, ...) = ConvertPythonListToCOSList(pythonfunction(param))

/// For example
ClassMethod Test()
{
  return $lb(11,22,33)
}

set $lb(a,b,c)=##class(Some.Class).Test()
write a,?5,b,?10,c ---> 11   22   33
Julius Kavay · Apr 5, 2024 go to post

I have no background information about your problem, but somehow I have the feeling, your have an organizational problem and not an isolational.

If one or more processes perform certain work under transaction and at the same time they feed a queue too ( I call it the WorkQueue) BEFORE the transaction is finished AND the queue is processed in parallel by an third process, then:
- either the third process (that with $order()) has to wait for the transaction to be finished or
- the process under transaction has to inform that third process about the outcome (i.e. rollback) of the transaction by feeding an second queue (I call this as the RolbackQueue).

The above implies, that
- before I put an item into the RollbackQueue, I would check, if it's already processed (from the WorkQueue), and if not, remove it from the WorkQueue and do not put it into the RollbackQueue
- in that third process, before processing an item, I would check, if that item is in the RollbackQueue. If yes, just do not process it (and remove from both queues)

As simple as that, merely I don't know your requirements.

Julius Kavay · Apr 2, 2024 go to post

A missing configuration is a good starting point... I think on Windows as well as on Linux, first you have to install the packages you are interested in, see here. Then you can import them.

Julius Kavay · Apr 2, 2024 go to post

It's not a "slight difference", it's a real difference, because you are using two different commands (a DO and an IF command)

set a = -1        // sets the variable a to -1
do $increment(a,1) zw "test" // increments <a> by 1, <a> is now 0
                             // the $increment() returns (the value of <a>)
                             // but the DO command do not expects a return value
                             // so the returned 0 is disposed
                             // hereinafter the next command on the line (zw) is executed
if $increment(a,1) zw "test" // increments <a> by 1, <a> is now 0
                             // the $increment() returns (the value of <a>) which is 0
                             // the IF command takes the returned value (0) but the value is
                             // "FALSE", hence the rest of the line is skipped (old style IF)
                             

If yo want to use the IF command and want both commands to be executed and have them on one line then:

// you have several options, a few of them
//
if $increment(a,1) { } zw "test" // empty IF-statement
if $increment(a,1)!1 zw "test"   // an IF with a forced outcome
if $increment(a,1)=a zw "test"   // if a=$increment(a,1) WON'T WORK !!!
//
// The only limit is only your imagination...

By the way, it's not a good idea using commands/functions with abbrevations and at the same time using mixed cases. In your examples I had to take a second look to realize, the one is a capital "i" and not a lowercase "L"

if $I(a,1) // increment a by 1
if $l(a,1) // return the subfieldcount from <a>, delimited by the character "1"
Julius Kavay · Apr 1, 2024 go to post

This works for me (pay attention to the semicolons!)

ClassMethod numpytest() As %String [ Language = python ]
{
 import numpy as np
 import math
 arr = np.array([1, 2, 3, 4, 5]); print(arr); x=np.random.randint(1, 15, 1) # generate a number between 1 and 15
 print("Random num=",x)
 y=math.pi
 print("This is pi:",y)
 x="hello world"
 print(x)
 a=2+5
 print("a=",a)
}

an the output is


USER>d ##class(Py.Demo).numpytest()
[1 2 3 4 5]
Random num= [13]
This is pi: 3.141592653589793
hello world
a= 7

USER>w $zv
IRIS for UNIX (Ubuntu Server LTS for x86-64) 2021.2 (Build 649U) Thu Jan 20 2022 08:49:51 EST
USER>