Julius Kavay · Aug 31, 2020 go to post

First, I think, you should check the above method...
You use the date variable two times

set date = $piece(...)
return date_" "...


I'm pretty shure, if the time in Vienna (Europe) is 00:30:00 then the coresponding UTC time is 23:30:00 previous day (standard time). But you never change the date variable!
As a timestamp:
2020-08-31T00:30:00+01:00 (Vienna, 31. August)
2020-08-30Z23:30:00 (UTC,  30.August)

Second (but this is just my very privete view), checking time(stamp) format should be done BEFORE calling the ConvertW3CToTimestampUTC() method and not in the method itself.

The holy trinity of programming is:
1) accept data (data input)
2) check the data
3) if the data is OK use it, else back to input (or return an error)

If you accpet data without checking, it means, every time, you intend to use those data, you have to check them again and again (as above), wich is not very efficient.

Julius Kavay · Aug 27, 2020 go to post

All ISC products (Ensemble, Cache, IRIS...) have an callin and callout interface via dll/so.  As a starting point, read this

https://docs.intersystems.com/healthconnectlatest/csp/docbook/DocBook.U…

and this

https://cedocs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?…

You will also need some experience in C/C++ or Delphi or in other language, where you can compile *.dll (Win) or *.so (Linux) files.

See the functions $zf(-3), $zf(-4), $zf(-5) and $zf(-6)

Julius Kavay · Aug 25, 2020 go to post

Create a FindLab() method in your ListLabCenter class, something like:

Method FindLab(start = 0, LabId = "", Center = "", Code = "")

{    set nc1=LabId="", nc2=Center="", nc3=Code=""

     for i=start+1:1:..Labs.Count() {

         set tmp=..Labs.GetAt(i)

         if tmp.LabId=LabId!nc1, tmp.Center=Center!nc2, tmp.Code=Code!nc3 return i

}

   quit 0

}

Then use it as follows:

write obj.FindLab(0,"A08829848","A088298480003","") to find a specific Lab

or 

set  center=0
for  { set center = obj.FindLab(center,,"A088298480003")
       quit:'center
       /* do something with center *./
}

to find all LabCenter objects where Center = "A088298480003"

Julius Kavay · Aug 24, 2020 go to post

ISO 8601 time format is either yyyy-mm-ddZhh:mm:ss (UTC time) or yyyy-mm-ddThh:mm:ss+TZ (local time)
where TZ is the time zone.

If both times are UTC time, then just compare.
If the times are local times and both times are in the same TZ then just compare
in elsecase either convert both times to UTC or one of the times to the TZ of the another time.
Then just compare.

The compare function is just a $select(), nothing more:

t1, t2 are the times, then

if $select(t1]t2:"t1 later", t1=t2:"equal", 1:"t1 is earlier")

or more general:

set result=$select(t1]t2:1, t1=t2:0, 1:-1)
if result>0 write "t1 is later then t2"
if result>=0 write "t1 is later or equal to t2"
...
Julius Kavay · Aug 20, 2020 go to post

If the class is in use (i.e. instantiated) then it will use the "old version".
So you have to take whatever  step(s) needed (stop production, stop Cache/IRIS) to unload the old version.

Julius Kavay · Aug 4, 2020 go to post

After more than 42 years of M-programming and in total of 48 years of programming experience I would say, if you need a class with about 1000 or more properties than something is wrong with your (database) design. There is nothing more to say. Period.

Julius Kavay · Jul 29, 2020 go to post

I assume,  your JSON is generated by converting a dynamic object to string, i.e.: 

do obj.%ToJSON()

In elsecase, it's created manually an there you could put as many quotes as you like around the numbers.

For the first case, just make a function which converts the numbers into string, see below:

test ;test for stringify
   set a={},a.Name="John",a.Age=47,a.xx="ab:1234",a.City="Boston",a.Year=2020
   set a."Arr"=[11,22,"aa","bb"]
   set a."Obj"={"aa":"bb"}

   write a.%ToJSON(),!
   write $$stringify(a).%ToJSON(),!
   quit

stringify(x)
{
   set y=x.%GetIterator()
   while y.%GetNext(.i,.v) {
     set t=x.%GetTypeOf(i)
     if t="number" { d x.%Set(i,v,"string") } elseif t="array"!(t="object") { do stringify(v) }
   }
   quit x
}

The output  is then:

do ^test
{"Name":"John","Age":47,"xx":"ab:1234","City":"Boston","Year":2020,"Arr":[11,22,"aa","bb"],"Obj":{"aa":"bb"}}
{"Name":"John","Age":"47","xx":"ab:1234","City":"Boston","Year":"2020","Arr":["11","22","aa","bb"],"Obj":{"aa":"bb"}}
Julius Kavay · Jul 15, 2020 go to post

Did you read the documentation?

TO_DATE(datestring [,format])

Your datestring is 5/8/2020 but your format is 'YYYY-MM-DD' which does not match '5/8/2020'!!

Julius Kavay · Jul 15, 2020 go to post

Maybe you want to use TO_DATE(...)  but if you want to use TO_CHAR() then you have to supply a correct argument, which is in this case a $HOROLOG value

SELECT TO_CHAR(0,'mm/dd/yyyy') ---01/12/1840

SELECT TO_CHAR(65575,'mm/dd/yyyy') ---15/07/2020

TO_CHAR(): A string function that converts a date, timestamp, or number to a formatted character string.

TO_DATE(): A date function that converts a formatted string to a date.

Julius Kavay · Jul 6, 2020 go to post

As always, it depends on ...
In this case on the definition of the date column.

a) if the date column is defined as %Date (i.e.: Property DateOfBirth As %Date) then the STORED value is just a number like 65567, because dates are stored in $Horolog format). 

b) if the date column is defined (for whatever reason) as %String (i.e.: Property DateOfBirth As %String) then the stored value is (as the type says) a string, and you can use the pattern operator to check the format.So the first question is, how is your date column defined?

Julius Kavay · Jul 5, 2020 go to post

You are a lucky man, you own a 300bd device!
I have one, but it's a newer type with 2400bd BUT with a fallback mode for 300bd too (if I recall it right).

Julius Kavay · Jul 5, 2020 go to post

This should be something like:

do httpRequest.EntityBody.Clear()

do httpRequest.EntityBody.Write("Your Data")

set status=do httpRequest.Post(...)
Julius Kavay · Jul 3, 2020 go to post

If I understand you correctly,  you want something like

select * from yourTablename where yourColumname %PATTERN '1.N'

i.e. where a column contains an integer greater or equal 0 (1.N means: one or more digits)

Julius Kavay · Jun 18, 2020 go to post

Thanks for the (finger board) link.

Until now, I didn't realise, that the tags are clickable. Sorry.
But in general, it's a good idea, before using an acronym, to spell it out first, to avoid misunderstandings

Julius Kavay · Jun 18, 2020 go to post

It would be very helpful to know, what CCR means.
https://acronyms.thefreedictionary.com/CCR knows  241 acronymes for CCR (not including such as Code Certification Rule, Code Certification Report, Customer Care Report and many more) .
If you filter for Science and Medicine (in the above site), you end up with 62 acronymes.

So what does your CCR mean?

Think about, not everybody works in the same field as you. Some people, like me, wrote countless applications for marketing, business management, trading, banking, scientific solutions and others, but never for healthcare.


justmy2cents

Julius Kavay · May 21, 2020 go to post

radix 10: mantisse *  10**exponent  (the  ISC  floating format)

radix 2 : mantisse * 2 ** exponent ($double/ IEEE 754 format )

Julius Kavay · May 21, 2020 go to post

The first part is easy... string, integer,... that's not the problem.

The second part, is it a date or not depends on the date format(s) you expect (or allow).

But for the first part, you can use this approach:

/// Return values
/// 0=empty, 1=string, 2=integer, 3=float/radix-10, 4=float/radix-2, 5=object
ClassMethod ValType(var) 
{
  if var="" quit 0
  if $isobject(var) quit 5
  quit $select($length(var)>254:1, 1:$translate($ascii($listbuild(var),2),1245678,1122334))
}

Of course, hardcore people would use just one line

ClassMethod ValType(var) { q $s(var="":0, $isobject(var):5, $l(var)>254:1, 1:$tr($a($lb(var),2),1245678,1122334)) }
Julius Kavay · May 21, 2020 go to post

Just to bring things in the right light

I'm a retired person but one needs some level of activity, that's the reason, why I'm here, do give answers and comments (and if the old customers need "a kind of prolonged help", I'm there too).
In early and mid seventies I programmed some so called "desktop computer", learned Cobol, IBM/360 assembler, Algol60 and Pascal as the preferred language on the university. Then, end of 1977 I met M (DEC's V4B).
Until the nineties thre were no IDEs, the first one I used was Serenji (from GeorgeJames) for MSM and Cache's Studio (with a lovely meat-chopper as the compiler icon). And there were no GitHub.
Anyway, we had great projects and great programms and fun.

The above means, I for myself do not need anything, neither Atelier nor VS-Code not even Studio but in case, I want to edit a class or routine, I prefer Studio.
During my active times, Studio was a great editor (and still it is!), sometimes I had more then one Studio open (at the same time, of course) to different Servers or different namespaces that's the reason why I requested the feature (server defined colors) some decades ago. The light red was the connection into, say a productio system, the green was into  a test namespace and blue, for example was the developmet system. A short glimpse at the display, if I saw red, my brain said, "keep fingers from keyboard", if I saw blue then my brain was ready for switch off... 

But I know several people working since decades with Studio only (and with the line editor before - I hope you know ^% and remember it?). Those people are (roughly) between 50 and 60 years old, worked (and still work) with whatever brand of M-Language (DTM, MSM, ISM, GT-M, Cache etc.) and never had contact to "modern" languages like java, C++, javascript, etc.
That's in my opinion the reason, why Atelier not so widespread as it deserved to be.

De facto, we have a two class society of M-programers

a) the young ones,
- they know everything about the current IDEs (Visual Studio, VS-Code, Eclipse, etc.
- they try to master (more or less) the M-World but
- their heart beats for languages as Java, Javascript, C++, Angular, Scala, etc.
- can't live without Git, GitHub, Bitbucket etc.
- they do not understand the essence/character of M-language (especially Cache/Iris)
- and think, MongoDB is the only NoSql-DB
- work few months (or one or two years) for a company, then change
- in the new company start over with another environment (Java, Angular, whatever)
- many are, I use to say, single-threaded: one project at time only
- etc.

b) and the old ones
- they grew up with the M-language
- they understand the power of M as a unity (language and database)
- most of them never used Git (and similar sites) for version control
- some of them never used a version control at all!
- most of them work with Windows (but this has historical reason)
- a few years before retirement there's no incentive to change to new IDEs

The real problem is not which IDE is in use, the real problem is to get people for M-programming, who then use an IDE. There are, I think, more old(er) then young M-Programer. I know for companies, they search for M-Programers but have dufficulties to find one.
That said, at the end of the day, you have to raise interesse for M-Language and M-Database. So you will need another means and not just a new IDE.
Maybe I'm wrong, but it's like comparing the M-Language and M-Database with a dinner by a start chef.
Nobody asks for the IDE they was in use, nobody asks for the pots and stoves they was in use.
After you leave the restaurant, the only thing that remains is, was the dinner good, were you satisfied.
Same goes the M-Language, does it can all things I need, will I be happy with it.
And yes, my answer is a big yes, definitive.

Julius Kavay · May 20, 2020 go to post

In my opinion the very first question is the support. a) Studio

  • it's installed (and updated) with Cache/IRIS
  • if I have a question, there is WRC b) VS-Code
  • it's not installed with Cache/IRIS
  • so I have to care about the installation
  • where to get the suitable files (and updates)
  • and I have to care about the "how to" install it
  • in a case of a problem, who am I contacting?
    1. the WRC? They will say, it's not our product
    2. the community? Are they always (24/7) there?

The current situation with VS-Code and the COS-plugin (my very private opinion) is

  1. it is assumed, that one is an experienced user of VS-Code
  2. there are no installinstuctions and user manuals

ad 1): assume, one day ISC says "VS-Code with COS plugin exists, so we can drop Studio". What should happen to all those COS-programers, how should they master VS-Code? Most of them have never ever heard about VS-Code? Keep in mind, not everybody grew up with mother's breast in one hand and an App in another !

ad 2): yes, there are some YouTube videos. On all videos I saw, VC-Code was already installed

Do not get me wrong, I'm not again VS-Code (the truth is, I would like to use it on my Linux) and I'm not for keeping Studio until eternity (because there is no Studio for Linux). But currently, Studio has some advantage (again, for me):

  • it's installed
  • I can have (and yes, I have) several connections
  • I can give each connection a different (background) color
  • I have the WRC (but I never had a Studio issue)
Julius Kavay · May 2, 2020 go to post

In case, you want some speed, use

set outArray = []

instead of 

set outArray = ##class(%DynamicArray).%New()

Your speed gain: ca. 50%

Julius Kavay · Apr 30, 2020 go to post

The %OpenId() method checks, if the object to be opened ALREADY open (in the current session/job). If it's open then it just returns with an success.  In other case, it loads it from the disk. 

If on your development system, for whatever reason,  there is an open instance  then you save 6.1 million times the "load" operation.

Julius Kavay · Apr 30, 2020 go to post

You are wondering about a SLOW production server and a FAST development machine?

I show you, how to turn your machines (production or development or both) into  a SLOW and FAST systems  by using your own measurement method.

ClassMethod Unclean()
{
   s p=##class(digi.packet).%OpenId("packet||5237")
   d ..runme4()
} 

ClassMethod Clean()
{
   d ..runme4()
}

Login to either of your systems and try the abowe methods in a terminal session. On my local system (of course, with an own persistent class)  the one method is 12 times faster then the other.

Maybe you have an "Unclean" situation on your development system...

Also, sometime it is worth to put a line like

do $system.OBJ.ShowObjects()

as a first line into the runme4() method

Julius Kavay · Apr 16, 2020 go to post

Why do you do make things such complicated?  You neither need  %ListOfDataTypes nor  %ZEN.proxyObject. If I see correctly, at the end of the query you have a characterstream, filled with JSON. Right? So just create this stream on-the-fly!

set json = ##class(%Stream.TmpCharacter).%New(), del=""

  &sql(declare ... )
  &sql(open queryONLWK01)
        
  do json.Write("[")
     for  {
        &sql(fetch queryONLWK01)
        quit:SQLCODE
        do json.Write(del_"{")
        do json.Write("""articlenumber"":" _ articlenumber _ ",") // (*)
        do json.Write("""amount"":" _ amount)
        do json.Write("}")
        set del=","
     }
     do json.WriteLine("]")

(*) if articlenumber is an alphanumeric value then you should do something like this

    do json.Write("""Aarticlenumber"":"_$$json(article)_",")

 where $$json(x) could be something like this ($zconvert() has  no JSON-mode in Cache-2013)
    

json(val) {
   for c="\","/","""",$c(8),$c(9),$c(10),$c(12),$c(13) set val=$replace(val,c,"\"_$tr(c,$c(8,9,10,12,13),"btnfr"))
   for  quit:'$locate(val,"[:cntrl:]",0,j,v)  set $e(val,j-1)="\u"_$e($zh($a(v)+65536),2,5)
   quit """"_$zcvt(val,"O","UTF8")_""""
}
Julius Kavay · Apr 8, 2020 go to post

OK, I installed VSCode together with the objectscript extension on my Linux. Now, I would like to play a little bit with this IDE, merlely for an VSCode beginner it's somewhat difficult. According to https://openexchange.intersystems.com/package/VSCode-ObjectScript there is a  settings file, ".vscode/settings.json",  to make a connection to my  iris server...  I located a file  ".config/Code/User/settings.json". After entering the connection info -this works now. Question, how or where can I enter a second (or even more) Cache/Iris servers?  Next, created a short test routine and saved, but it's not compiled. How to compile it?

I have never used VSCode before, I work since beginning with the Cache-Studio (this means some 20 years or so) and other twenty years before with other editors. Is there somewhere a short tutorial or some other info? It seems, I will need some time to catch this "modern" stuff ;-)

Thank you.
 

Julius Kavay · Apr 2, 2020 go to post

You could try this way:

Class My.Table2 Extends %Persistent
{

Property Name As %String;

Property Age As %Numeric;

Property City As %String;

Property Phone As %String;

ClassMethod Test()
{
    // This is your Data-Object...
    set data=[]
    do data.%Push({"Name":"Joe", "Age":44, "City":"Boston", "Phone":"1-234-4567"})
    do data.%Push({"Name":"Ron", "Age":48, "City":"Dallas", "Phone":"1-234-5678"})
    do data.%Push({"Name":"Eve", "Age":40, "City":"Miami",  "Phone":"1-234-4567"})
    
    do data.%Push($lb("Tommy", 50, "New York", "1-345-6789"))
    do data.%Push($lb("Alexa", 35, "Portland", "1-567-8901"))

    // Now insert all the above data into your table...
    if 'data.%Size() quit
    
    set cnt=0, size=data.%Size()
    while $i(cnt)<=size {
        set rowData=data.%Get(cnt-1)
        &sql(
            INSERT INTO My.Table2 (Name,Age,City,Phone)
            VALUES (My.Table2_DataProvider(:rowData), :row(2), :row(3), :row(4))
        )
    }
} 

ClassMethod DataProvider(rowData) As %String [ PublicList = row, SqlProc ]
{
    kill row
    if $isobject(rowData) {
        set it=rowData.%GetIterator()
        while it.%GetNext(,.val) { set row($i(row))=val } 

    } else {
        for i=1:1:$ll(rowData) { set row(i)=$lg(rowData,i) }
    }
    quit row(1)
}

Storage Default
{
<Data name="Table2DefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>Name</Value>
</Value>
<Value name="3">
<Value>Age</Value>
</Value>
<Value name="4">
<Value>City</Value>
</Value>
<Value name="5">
<Value>Phone</Value>
</Value>
</Data>
<DataLocation>^My.Table2D</DataLocation>
<DefaultData>Table2DefaultData</DefaultData>
<IdLocation>^My.Table2D</IdLocation>
<IndexLocation>^My.Table2I</IndexLocation>
<StreamLocation>^My.Table2S</StreamLocation>
<Type>%Library.CacheStorage</Type>
} }

Create your data for insert and then

INSERT into yourtable (Prop1, Prop2, ...)

VALUES (sqlProcForTheFirstValue(), :localVarForOtherValues(2), :localVatForOtherValues(3),...)

see the above example.

Take care of the sequence of INSERT names and row(i) values.

Julius Kavay · Apr 1, 2020 go to post

Oh, I just see now, I forgot to add the two test lines

Enter into browser : http://localhost:52773/csp/user/DC.Upload.cls?pswd=123&rpt=456

Enter into Terminal:

IDEV:USER>write ##class(DC.Download).GetFile(123,456,"/tmp/inpfiles/")  ==> 1