Julius Kavay · Jan 14, 2021 go to post

By the way, the above method returns the OS, Cache or IRIS was built for.

Because you can only install a product (Cache or IRIS) on a OS for which the product was built, so the above method returns the OS where your product  runs.

Julius Kavay · Jan 11, 2021 go to post

I see right now,  cut-and-paste without looking-and-checking isn't good! Sorry, for some stupid reason, I copied the wrong lines. The correct ones are:

ClassMethod Diamond(n)
{
   f n=1:1:n w ! f j=1:1:n,n-1:-1:1 w !?n-j f i=1:1:j,j-1:-1:1 w i
}

ClassMethod Infinite(n)
{
   f n=1:1:n w ! f j=1:1:n,n-1:-1:1 w !?n-j f i=1:1:j,j-1:-1:1 w i#10
}

Also, Dimond() works from 0 thru 9 (and not thru 10). The line lengths (with 63 and 66 chars) were correct. Finally, the correct output:

do ##class(DC.CodeGolf).Diamond(3)

1

 1
121
 1

  1
 121
12321
 121
  1
Julius Kavay · Jan 11, 2021 go to post

OK, you want it short and endless? You can get it!

Diamond() works from 0 thru 10 using 63 chars

Infinite() works from 0 thru Cache's maxint and has 66 chars of source code.

ClassMethod Diamond(n)
{
   f n=1:1:n w ! f j=1:1:n,n-1:-1:1 w !?n-j f i=1:1:2*j-1 w i#-j+j
}

ClassMethod Infinite(n)
{
   f n=1:1:n w ! f j=1:1:n,n-1:-1:1 w !?n-j f i=1:1:2*j-1 w i#-j+j#10
}
 

Some output

USER>

USER>d ##class(DC.CodeGolf).Diamond(0)

USER>d ##class(DC.CodeGolf).Diamond(3)

1

 1
121
 1

  1
 121
12312
 121
  1
USER>d ##class(DC.CodeGolf).Infinite(13)

1

 1
121
 1

  1
 121
12312
 121
  1

   1
  121
 12312
1234123
 12312
  121
   1

    1
   121
  12312
 1234123
123451234
 1234123
  12312
   121
    1

     1
    121
   12312
  1234123
 123451234
12345612345
 123451234
  1234123
   12312
    121
     1

      1
     121
    12312
   1234123
  123451234
 12345612345
1234567123456
 12345612345
  123451234
   1234123
    12312
     121
      1

       1
      121
     12312
    1234123
   123451234
  12345612345
 1234567123456
123456781234567
 1234567123456
  12345612345
   123451234
    1234123
     12312
      121
       1

        1
       121
      12312
     1234123
    123451234
   12345612345
  1234567123456
 123456781234567
12345678912345678
 123456781234567
  1234567123456
   12345612345
    123451234
     1234123
      12312
       121
        1

         1
        121
       12312
      1234123
     123451234
    12345612345
   1234567123456
  123456781234567
 12345678912345678
1234567890123456789
 12345678912345678
  123456781234567
   1234567123456
    12345612345
     123451234
      1234123
       12312
        121
         1

          1
         121
        12312
       1234123
      123451234
     12345612345
    1234567123456
   123456781234567
  12345678912345678
 1234567890123456789
123456789011234567890
 1234567890123456789
  12345678912345678
   123456781234567
    1234567123456
     12345612345
      123451234
       1234123
        12312
         121
          1

           1
          121
         12312
        1234123
       123451234
      12345612345
     1234567123456
    123456781234567
   12345678912345678
  1234567890123456789
 123456789011234567890
12345678901212345678901
 123456789011234567890
  1234567890123456789
   12345678912345678
    123456781234567
     1234567123456
      12345612345
       123451234
        1234123
         12312
          121
           1

            1
           121
          12312
         1234123
        123451234
       12345612345
      1234567123456
     123456781234567
    12345678912345678
   1234567890123456789
  123456789011234567890
 12345678901212345678901
1234567890123123456789012
 12345678901212345678901
  123456789011234567890
   1234567890123456789
    12345678912345678
     123456781234567
      1234567123456
       12345612345
        123451234
         1234123
          12312
           121
            1
USER>
Julius Kavay · Jan 10, 2021 go to post

Maybe there are shorter solutions, but somebody must start the game... (I hope, class- and method name does not count)

Class DC.CodeGolf Extends %RegisteredObject
{ 
/// Diamonds from 1 thru 9 (max)
ClassMethod Diamond9(n)
{
  f n=1:1:n f i=1:1:n-1,n,n-1:-1:1 {s a=$e(1234567890,1,i-1) w ?n-i,a,i,$re(a),!} w !
} 

/// Diamonds from 1 thru 16 (max)
ClassMethod Diamond16(n)
{
   f n=1:1:n f i=1:1:n-1,n,n-1:-1:1 {s a=$e("1234567890abcdef",1,i) w ?n-i,a,$re($e(a,1,*-1)),!} w !
} 

}
Julius Kavay · Dec 31, 2020 go to post

If your input (date and time) format is the same as the example you provided,

set inpDateTime = "12/10/2019 21:41"

then use this one:

write  $tr($zdt($zdth(inpDateTime,1,2),8,1),": ")  --> 20191210214100

and if you want it "a little bit faster", the try this one

write  $tr("abcdefghijkl00","ef/gh/abcd ij:kl",inpDateTime)  --> 20191210214100

and you will get a speed gain of over 33%

Happy New Year!

(But this time, without Covid-19, if possible!)

Julius Kavay · Dec 9, 2020 go to post

SystemAdministration-->Configuration-->NationalLanguageSettings-->Defaults/Defs/Imp/Exp

First you have to start with a predefined/default table, copy under a new name, select this new table and click on the properties.

Julius Kavay · Dec 9, 2020 go to post

To make things clear, the mentioned popup message should be seen by someone, who is (most of the time) working on the servers console. Is it so? Or you want to popup this message on an arbitrary desktop, where a user works?

If I need to send a message to a user, either I send an email or I activate a popup message in my client (I have a very special client-UI).

By the way, you wrote, I quote "an email is NOT a realistic expectation and far from reliable". Today, (almost) everybody has a smartphone and I think, if somebody does not read an email, he/she won't read thos popup messages either. Of course, you should send short plain text messages and not those fancy bloated colorful emails.

So, a way to a solution... you could write a  small program (C++, VB, Delphi, etc.) which is started after user login.  The program should listen on a TCP port for messages from an arbitrary Cache/Iris (possibly background) application. If such a message arrives, this helper program changes to foreground, displays the message with or without an OK button. That's it.

Julius Kavay · Dec 9, 2020 go to post

Store only hashed passwords... that's all.

Class DC.MyUsers Extends %Persistent
{ 
Property Name As %String;
Property Password As %String;
Property passHash As %String [ Internal, Private, ReadOnly ];
Property passSalt As %String [ Internal, Private, ReadOnly ];
Parameter ITER = 1024;
Parameter LENGTH = 20;
Method PassCheck(psw) As %Boolean
{
  set salt = $system.Encryption.Base64Decode(..passSalt) 
  set hash = $system.Encryption.Base64Decode(..passHash)
  quit $system.Encryption.PBKDF2(psw, ..#ITER, salt, ..#LENGTH)=hash
}
Method PasswordSet(psw) As %Status
{
  // optionally, quality/requirement-check
  if '..pswQuality(psw) quit $$Error^%apiOBJ(5001,"Poor password quality")
  
  set salt=$system.Encryption.GenCryptRand(8)
  set hash=$system.Encryption.PBKDF2(psw, ..#ITER, salt, ..#LENGTH)
  set i%passHash=$system.Encryption.Base64Encode(hash)
  set i%passSalt=$system.Encryption.Base64Encode(salt)
  quit $$$OK
}

Method pswQuality(psw) As %Boolean
{
  quit 1
}
}

Julius Kavay · Dec 9, 2020 go to post

There are two points,

the first (catchwords: server, interaction) was already answered by Dmitriy Maslennikov 

the second is your 10 second popup button.

In my over 40 years of IT-experience, there is one thing (along with others) I have learnd, is: every timeout is wrong, but messages with timeouts are evil! Whatever time you use, it's either too short or too long.

Imagine, the phone is ringing abd the user has a hot 20 minute discussion on the phone, in the meantime, your popups comes and goes! Unseen! Sometimes several times!

The only resonable solutions are,

- if the message is (just) informative and the message text never changes, then put it into a logfile and show nothing.  If the message text is a variable text ("Data is saved" vs. "Can't save: No disk space available") then do the popup with one button (or textinput), see below, but DO NOT use timeouts! 

- if the situation allows the user to choose between multiple answers, then let the popup window with those OK, YES, NO, CANCEL, etc. buttons stay there, as long as the user chooses one of them, or as an alternative (application dependent), offer an ordinary text input and at sometime the user types the answer and pushes the enter key.

Messages with (possible with short) timeouts requires a user all the time gazing on the display - which you can't expect.

justmy2cents

Julius Kavay · Dec 5, 2020 go to post

Just in addition to what Robert wrote,  Mumps has the concept of multithreading for  over 50 years! Long before C++ and Java was born.

justmy2cents

Julius Kavay · Dec 4, 2020 go to post

Instead of inserting debug_macros, try Intersystems TRACE utility.

write $$DIR^TRACE("c:\Temp\") ; to set an output directory
write $$ON^TRACE(jobnr) ; the pid of the process you want to trace
; zn "appNamespace"
; do ^yourProgram
; zn "%SYS"
write $$OFF^TRACE(jobnr) ; to stopp the trace
do ^TRACE ; to display the trace result

TRACE displays the function-/method-calls with arguments.

Julius Kavay · Nov 30, 2020 go to post

I said nothing about a documentation. I don't do it even devil

By the way, what's the definition of an empty set?

Answer:

4(%@3%4@')6%.@"9@).4%23%#4)/.@/&@-5-03@02/'2!-%23@!.$@4(%)2@$/#5-%.4!4)/.

Julius Kavay · Nov 24, 2020 go to post

After seeing several solutions I got the idea to make a comparison.
The bottom line is, it's advisable to check how an algorithm (or function or method etc.) performs over another.
So try the below program snippet... you will be surprised!

Test //
   s date="20201121090000"
   s new=""
   s t0=$zh

   f i=1:1:1E6 s new=$e(date,1,4)_"-"_$e(date,5,6)_"-"_$e(date,7,8)_" "_$e(date,9,10)_":"_$e(date,11,12)_":"_$e(date,13,14)
   s t1=$zh
   f i=1:1:1E6 s new=$tr("abcd-ef-gh ij:kl:mn","abcdefghijklmn",date)
   s t2=$zh
   f i=1:1:1E6 s new=$zd($zdh($e(date,1,8),8),3)_" "_$e(date,9,10)_":"_$e(date,11,12)_":"_$e(date,13,14)
   s t3=$zh
   f i=1:1:1E6 s new=$system.SQL.TOTIMESTAMP(date, "YYYYMMDDHHMISS") 
   s t4=$zh
   f i=1:1:1E6 &SQL(SELECT TO_TIMESTAMP(:date,'YYYYMMDDHHMISS') INTO :new)
   s t5=$zh

   w "$e() only",?12,t1-t0,!
   w "$tr()",?12,t2-t1,!
   w "$e()+$zd()",?12,t3-t2,!
   w "SQL/class",?12,t4-t3,!
   w "SQL/static",?12,t5-t4,!
   q

Of course, the results will depend on hardware,  on Cache/IRIS version and on utilisation of your system

Julius Kavay · Nov 24, 2020 go to post

Use $tr() in backward mode

set date=" 20201121090000"
write $tr("abcd-ef-gh ij:kl:mn","abcdefghijklmn",date)  --> 2020-11-21 09:00:00
Julius Kavay · Nov 15, 2020 go to post

Just to see things clearer, you do this test directly on Cache server (i.e. local)?
And you have logged in with your local  (and not, for example with an domain\kevin) account? Cache runs also under the same local "kevin" account?

Julius Kavay · Nov 15, 2020 go to post

Hello Kevin,

in most of the cases (but not always) the reason for this is the exhaustion of TCP ports, see

https://docs.microsoft.com/en-us/windows/client-management/troubleshoot…

too. Grab a windows command prompt and start with

netsh int ipv4 show dynamicport tcp

this shows you how many ports you have.

netstat -ano | find "TCP"

shows you all the TCP ports in use (including the process numbers) and

netstat -an | find "TCP" | find "CLOSE"

shows you all the bad guys.

If this is your problem then the solution is:

- increase the number of ports (if it's possible)

- reduce the cases, where a new port is needed and immediate closing of unneeded ports

Julius Kavay · Nov 11, 2020 go to post

If your number is an integer

write $extract(1E10 + 12345, 2, 11)   // if N is a fixed value, here N = 10

or

write $extract("1E"_N + 12345, 2, N+1)  // if N is variable

The game ends if your number has more than 18 (decimal) digits!

Julius Kavay · Nov 11, 2020 go to post

A quick and dirty way:

set ^|"%SYS"|%SYS("SystemMode")="TEST" // or "LIVE" or "DEVELOPMENT" or "FAILOVER"
Julius Kavay · Nov 9, 2020 go to post

Sorry, I don't quite get you.

One used to say, if I don't understand something, then this something is either too complicated or very simple. So where belongs your case?

Julius Kavay · Nov 9, 2020 go to post

I'm not sure about what you mean with "unlimited amount of indexes"...

first, there is a limit of 255 for the parameters, this means you can have 

do ..setValue(key1,key2,...key254,value)

but you can't have 

 do ..setValue(key1,key2,...key254,key255,value)

second, if you meant to have many-many values (in sense of, we have to much data)

  do ..setValue(key1,value1)
  do ..setValue(key2,value2)
  // etc.

then, at some point you will reach the and of the available RAM (incl. swap/paging space.  Usually swap/paging space is much less then the space for the real data).

thirdly, if you meant variable number of indexes (in sense of variable number of parameters) then I can just refine Robert's solution to

ClassMethod setValue(params...) As %Status
{
   set base=$name(%session.Data)
   for i=1:1:params-1 { set base=$name(@base@(params(i)))
   set @base=params(params)
   quit 1
}

In case, you have both, variable number of indices and too much data then you could use a mix of the above with data placed in a temp file.

Somewhere, at the program start, let you point %sessionData to a temp file

   set %sessionData=$nam(^||SessionTemp)

and then consider this in the setValue method

ClassMethod setValue(params...) As %Status
{
   set bas=%sessionData
   ...
}

Of course, in your application, you have to change only those %session.Data(...) to  @%sessionData@(...)  which uses the "variable-big-data" part of %session.Data(). All other (the standard ones) stay unchanged.

Julius Kavay · Oct 21, 2020 go to post

Oh yes,  DATEADD(ms, ....) works, but

select  'Arrival Time' ... 

won't work. This gives the string constant of "Arrival Time"

Julius Kavay · Oct 21, 2020 go to post

Sorry, I didn't noticed the space char... maybe I need new glasses ;-)

But yes, if you have some unusual property names, then you need double-quotes ($char(34), he used $char(39))

Julius Kavay · Oct 21, 2020 go to post

I'm pretty sure, you intended to write

select COUNT (Arrival Time) FROM dbo.table where Arrival Time < DATEADD(ms, CONVERT(int,LEFT(1603173432000, 20)), '1970-01-01 00:00:00')

i.e. do not place propertynames under quote

By the way, I don't know, how many records you have in your table, but if you have thousands or millions of records, consider to compute constant things just one time!

DATEADD(ms, CONVERT(int,LEFT(1603173432000, 20)), '1970-01-01 00:00:00')

is a constant value, it's not neccessary to compute it for each record! Also, in this particular case, CONVERT() and LEFT() are also not needless, 1603173432000 is the value, and it is an integer.

Julius Kavay · Oct 19, 2020 go to post

As Robert said, XSLT... but as he indicated too, in this case XSLT would be an overkill.

But there is also a "forgotten" function, $locate() too for such cases! This function is almost never used in DC examples. Two or three lines of code, and you have the perfect solution... but for "simple" cases only. Beware of nested tags!

SetFixedValue(str,tag,value) Public
{
   set i=0, t="(?i)<"_tag_">[^<]+", s=$l(tag)+2
   while $locate(str,t,i,j,v) { set $e(str,j+s-$l(v),j-1)=value, i=j+s-$l(v)+$l(value) }
   quit str
}

It works like a charm...

set a="<Name>..."
write $$SetFixedValue^test(a,"rollno","***")  -->  <Name>ABC</Name><RollNo>***</RollNo><Name>XYZ</Name><RollNo>***</RollNo><Name>xyz</Name><RollNo>***</RollNo>
Julius Kavay · Oct 17, 2020 go to post

During the installation process a stripped-down version of the apache  web server will be installed too (to serve the management portal and for test purposes). This is the CSP-System (CSP means CacheServerPages). Enter a password, you will need it rarly or almost never.

Julius Kavay · Oct 16, 2020 go to post

To say it simply, you did this:

set x=123
set x=456

and just  see 456 but not 123!

What you (probably) want to do is:

set x(1)=123
set x(2)=456

I added one more "patient item" to your JSON and a simple test class

Class DC.PR1 Extends %RegisteredObject
{ 
ClassMethod GetDataFromTestStream() [ PublicList = (myGuid, myId, myForename, mySurname, myDOB, nokName, nokRelation, nokTel, nokEmail) ]
{
   set xData=##class(%Dictionary.CompiledXData).%OpenId(..%ClassName(1)_"||Test")
   try { set obj={}.%FromJSON(xData.Data) }
   catch e { set obj="",err=e.Name }
   if 'obj { 
      write "Error: ",err,!
      quit ""
   }  

   // Put all properties in the corresponding SUBSCRIPTED variable
   //
   for i=0:1:obj.%Size()-1 { // loop over patients (top array)
      set patient=obj.%Get(i).patient

      set myGuid(i)=patient.guid
      set myId(i)=patient.id
      set myForename(i)=patient.forename
      set mySurname(i)=patient.surname
      set myDOB(i)=patient.dateOfBirth

      for j=0:1:patient.NOK.%Size()-1 { // loop over NOKs of the i-th patient
         set nok=patient.NOK.%Get(j)

         set nokName(i,j)=nok.NOKname
         set nokRelation(i,j)=nok.NOKrelationship
         set nokTel(i,j)=nok.telephone
         set nokEmail(i,j)=nok.email

         // instead of storing each property, you could
         // process them hier...
      }
   }
} 

XData Test [ MimeType = application/json ]
{
[
  {
    "patient":
      {
        "guid":"12345",
        "id":12345,
        "forename":"Joe",
        "surname":"Bloggs",
        "dateOfBirth":"2002-12-10T00:00:00Z",
        "NOK":[
                {
                   "NOKname":"Alison Bloggs",
                   "NOKrelationship":"Wife",
                   "telephone":"02081234567",
                   "email":"alison@bloggs.com"
                },
                {
                   "NOKname":"Peter Bloggs",
                   "NOKrelationship":"Father",
                   "telephone":"02081234567",
                   "email":"Peter@bloggs.com"
                }
              ]
      }
  },
  
  {
    "patient":
      {
        "guid":"212345",
        "id":212345,
        "forename":"John",
        "surname":"Blue",
        "dateOfBirth":"2003-12-10T00:00:00Z",
        "NOK":[
                {
                   "NOKname":"Aline Blue",
                   "NOKrelationship":"Wife",
                   "telephone":"032081234567",
                   "email":"aline@blue.com"
                },
                {
                   "NOKname":"Peter Blue",
                   "NOKrelationship":"Father",
                   "telephone":"032081234567",
                   "email":"Peter@blue.com"
                }
              ]
      }
  }

]
} 

}


Try it in a terminal session:

do ##class(DC.PR1).GetDataFromTestStream()

zwrite

The output  should be:

myDOB(0)="2002-12-10T00:00:00Z"
myDOB(1)="2003-12-10T00:00:00Z"
myForename(0)="Joe"
myForename(1)="John"
myGuid(0)=12345
myGuid(1)=212345
myId(0)=12345
myId(1)=212345
mySurname(0)="Bloggs"
mySurname(1)="Blue"
nokEmail(0,0)="alison@bloggs.com"
nokEmail(0,1)="Peter@bloggs.com"
nokEmail(1,0)="aline@blue.com"
nokEmail(1,1)="Peter@blue.com"
nokName(0,0)="Alison Bloggs"
nokName(0,1)="Peter Bloggs"
nokName(1,0)="Aline Blue"
nokName(1,1)="Peter Blue"
nokRelation(0,0)="Wife"
nokRelation(0,1)="Father"
nokRelation(1,0)="Wife"
nokRelation(1,1)="Father"
nokTel(0,0)="02081234567"
nokTel(0,1)="02081234567"
nokTel(1,0)="032081234567"
nokTel(1,1)="032081234567"

And don't forget, you may have more then one "patient" item in your JSON, so either consume the data in the inner loop oder place them in a two-dimensional array.

HTH

Julius Kavay · Oct 14, 2020 go to post

$SYSTEM returns the (local) name of a machine (where Cache/IRIS is installed), a colon, and the instance name.
Sorry, my mistake.