- Log in to post comments
Thanks for the adjustment, I looked at the very first line (odbc format, which is 3).
Anyway, the $tr() construct is called the $tr()-backwards format, I found this on the internet more then ten years ago... so the credit goes to a unknown inventor.
$tr(targetPattern, sourcePattern, sourceValue) --> targetValue- Log in to post comments
it's easy, without AM/PM
write $tr("abcd-ef-gh ij:kl:00","abcdefghijkl",202010011000),! // ODBC-Format
write $tr("gh.ef.abcd ij:kl:00","abcdefghijkl",202010011000),! // German-Format
write $tr("ef/gh/abcd ij:kl:00","abcdefghijkl",202010011000),! // US-Formatset datetime=202010011000
set tmp=$tr("abcdefgh ij:kl:00","abcdefghijkl",datetime) // or using $e(...)
set tmp=$e(datetime,1,8)_" "_$e(datetime,9,10)_":"_$e(datetime,11,12)_":00" write $zdt($zdth(tmp,3),1,3)
- Log in to post comments
To make your (and others) life easier... here is a simple class to display a dynamic object (or dynamic array).
/// A general class for various helper functions
///
Class %zapi.utils Extends %RegisteredObject
{ /// Show an dynamic array or object
///
/// obj : the dynamic object (or array) you want to display
/// deep: max path depth, 0 (default) = display all
/// find: if given, (a part of) a property name
ClassMethod ShowObj(obj, deep = 0, find = "")
{
new %seen
do ..shObj(obj,deep-1,find,"<obj>")
}
ClassMethod shObj(obj, deep, find, path) [ Internal, Private ]
{
set path=path_"."
set itr=obj.%GetIterator()
while itr.%GetNext(.prop,.val) {
set:obj.%IsA("%DynamicArray") prop="%Get("_prop_")"
set prop=path_prop
if $isobject(val) {
if $data(%seen(+val)) { set val="same as --> "_%seen(+val)
} else { set %seen(+val)=prop }
}
if $isobject(val),deep { do ..shObj(val,deep-1,find,prop)
} else { write:prop[find prop,": ",val,! }
}
}
}
With our old pData, use it as follows:
write pData
[{"patient":{"guid":"12345","id":12345,"forename":"Joe","surname":"Bloggs","dateOfBirth":"2002-12-10T00:00:00Z"},"visit":[{"guid":45678,"date":"2020-01-10","reason":"other"},{"guid":45679,"date":"2020-01-11","reason":"routine"}],"
documentAttachments":[{"guid":"23432","id":152,"catergory":"notes"},{"guid":"23433","id":153,"catergory":"summary"}]}]
set obj={}.%FromJSON(pData)
do ##class(%zapi.utils).ShowObj(obj)If you don't want to write each time a novel, you can create your own "show object" command too.
Insert the following line into %ZLANGC00 routine (if %ZLANG doesn't exists, create it):
ZSHO(obj, deep=0, find="") Public { do ##class(%zapi.utils).ShowObj(obj,deep,find) }and Voila!, you got a short command to display dynamic objects
zsho obj // display the whole object
zsho obj:2 // display up to second nesting level
zsho obj::"visit" // display all occurrences of property visit
zsho obj::"guid" // display all occurrences of property guidThe outputs of the above commands are
USER>zsho obj
<obj>.%Get(0).patient.guid: 12345
<obj>.%Get(0).patient.id: 12345
<obj>.%Get(0).patient.forename: Joe
<obj>.%Get(0).patient.surname: Bloggs
<obj>.%Get(0).patient.dateOfBirth: 2002-12-10T00:00:00Z
<obj>.%Get(0).visit.%Get(0).guid: 45678
<obj>.%Get(0).visit.%Get(0).date: 2020-01-10
<obj>.%Get(0).visit.%Get(0).reason: other
<obj>.%Get(0).visit.%Get(1).guid: 45679
<obj>.%Get(0).visit.%Get(1).date: 2020-01-11
<obj>.%Get(0).visit.%Get(1).reason: routine
<obj>.%Get(0).documentAttachments.%Get(0).guid: 23432
<obj>.%Get(0).documentAttachments.%Get(0).id: 152
<obj>.%Get(0).documentAttachments.%Get(0).catergory: notes
<obj>.%Get(0).documentAttachments.%Get(1).guid: 23433
<obj>.%Get(0).documentAttachments.%Get(1).id: 153
<obj>.%Get(0).documentAttachments.%Get(1).catergory: summary
USER>zsho obj:2
<obj>.%Get(0).patient: 11@%Library.DynamicObject
<obj>.%Get(0).visit: 2@%Library.DynamicArray
<obj>.%Get(0).documentAttachments: 21@%Library.DynamicArray
USER>zsho obj::"visit"
<obj>.%Get(0).visit.%Get(0).guid: 45678
<obj>.%Get(0).visit.%Get(0).date: 2020-01-10
<obj>.%Get(0).visit.%Get(0).reason: other
<obj>.%Get(0).visit.%Get(1).guid: 45679
<obj>.%Get(0).visit.%Get(1).date: 2020-01-11
<obj>.%Get(0).visit.%Get(1).reason: routine
USER>zsho obj::"guid"
<obj>.%Get(0).patient.guid: 12345
<obj>.%Get(0).visit.%Get(0).guid: 45678
<obj>.%Get(0).visit.%Get(1).guid: 45679
<obj>.%Get(0).documentAttachments.%Get(0).guid: 23432
<obj>.%Get(0).documentAttachments.%Get(1).guid: 23433 Have a nice day...
- Log in to post comments
It's simple and easy... just follow the picture (I meant, the JSON string):
[
{
"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"
}
]
}
}
]
Now we create a dynamic Object:
set obj = {}.%FromJSON(pData)Your pData is a JSON-Array, where the (array)elements/items are objects
(we have just one elemment), so we can say:
set item = obj.%Get(0)This item object has a patient property, which is an object, so we go one step deeper
set item = obj.%Get(0)
set patient = item.patient
set patient = obj.%Get(0).patient // same as aboveOur patient object has properties like guid, id, forename, surname, dateOfBirth and NOK.
The property NOK itself is, again, an array where the array elements/items are objects.
set nok = patient.NOK
set nok = obj.%Get(0).patient.NOK // same as aboveNow, we know, our property NOK is an array, so we have to take of those elements. This nok elements are objects, so we can take the properties (NOKname, ...email):
set nokItem = nok.%Get(0)
write nokItem.NOKname," ",nokItem.email,!or, all in one line
write obj.%Get(0).patient.NOK.%Get(0).NOKname,!Now all together, a small routine to print out all NOKxxxx properties:
set obj = {}.%FromJSON(pData)
for i=0:1:obj.%Size()-1 { // loop over pData items grabbing patient props
set patient = obj.%Get(i).patient
for j=0:1:patient.NOK.%Size()-1 { // now we loop over all NOK items
set nok = patient.NOK.%Get(j)
write nok.NOKname,!
write nok.NOKrelationship,!
}
}So I hope, you see the light at the end of the tunnel...
- Log in to post comments
According to above sample data, your JSON-Data (pData) is :
- an array of one or more objects
- the object(s) have "patient", "visit" and "documentAttachments" as properties
- the "patient" property is an object and has "guid", "id", "forename", "surname", and "dateOfBirth" as properties
So, to get the "forename" property, you have to do something like:
set object = {}.%FromJSON(pData) // create a JSON-Object from JSON string
write object.%Get(0).patient.forename // or step by step
set firstArrayItem = object.%Get(0) // first array item is 0
set patient = firstArrayItem.patient // this is the patioent object
write patient.forename // now you have the name
write patient.surname // and other propertiesBy the way, the "visit" property is also an array! To list all the "reason" properties, you have to write a loop:
set visit = firstArrayItem.visit // the first visit
for i=0:1:visit.%Size()-1 {
write visit.%Get(i).reason,!
}Your output will be:
other
routineI hope, things are now more clear?
- Log in to post comments
The problem is obvious, your set object = ##class(%DynamicAbstractObject).%FromJSON(pData) won't work, because pData contains an invalid JSON!
1) "visit":[{"guid":45678" <-- surplus QUOTE-char, maybe a typo
2) reason":"other"} {"guid":45679 missing comma between } and {
3) "catergory":"notes"} {"guid":"23433" missing comma between } and {
You can use https://jsonlint.com/ to check your JSON string.
- Log in to post comments
The above should work, so the question is, what is the content of pData. Do you can share a sample with us?
Also, you can write the above somewhat shorter too and you can access all the properties either via propertyname or via the %Get() method.
I assume, you have a string like this:
set pData="{""Name"":""Joe"", ""Age"":50}"the you can do this
set object={}.%FromJSON(pData)
write object.Name // Joe
write object.%Get("Name") // Joe
write pData // {"Name":"Joe", "Age":50}- Log in to post comments
Now we have the year 2020 (but I will discard this year from my life because of Covid-19) but our loved COS wasn't in this year nor in the very past years invented.
COS (better, the predecessor, M ) has more then 50 years under his belt, it was created in the late sixties - in a time, where RAM was rare, expensive and therefore it was measured in units of kilobyte!
If I recall correctly, at end of the seventies, we had some customers on a PDP-11/34 with less then 64KB of memory, one or two 2kb partitions and some "big" partitions with 4KB or 6KB, the rest was for disk buffers (512 byte each) and DEC's V4b (which acted as OS, database and language interpreter).
Hence the ability of COS for excessive abbreviations and if you get an old routine, written by an old school programmer (like me) then you will see there (mostly) abbreviated commands, postconditions and (local-/globalvariable, label and routine) names, consisting just a few letters (one to three, maybe four).
Unfortunately, this (abbreviated commands, short names) happens to me in this days too - you know, old habits never die.
There is a wonderful (and at the same time a horrific) example of such a routine: one letter names, everything abbreviated:
https://stackoverflow.com/questions/4151554/need-mumps-sample-code/4430…
clock ;; a digital clock from
;; https://stackoverflow.com/questions/4151554/need-mumps-sample-code/4430996
;;
Q N R,Q,C,D,E,W,B,G,H,S,T,U,V,F,L,P,N,J,A S N=$G(N),Q='N,F=Q+Q,P=F+F,W=$L($T(Q))
S W=$E(W,Q),S='N_+N,W=W-F*S,L=$G(L),R=$C(Q_F_P),R(F)=$C(F+Q_F),R(P)=$C(W-F) W #
S T=$E($T(Q+F),F,W\S)_$C(W+S+F) X T S B=$P(T,$C(P_P),F),C=B\(W*W),D=B-(C*W*W)\W
F G=S-Q:F:S+F+Q S E=B-(C*W*W+(D*W)),H=$E($T(Q),G),@H=$S(@H<S:'Q,Q:N)_@H,T=C_D_E
F A=Q:Q:W\S S J=$E(T,A),C(F)=$S(J>(F+Q)&(J<(S-F)):Q,Q:+N),C(P)=$S(J#F:Q,Q:+N) D
.S C(Q)=$S(J<(S-F):+N,Q:Q),C(F+Q)=$S(J>Q&(J<(S-F))&(J'=(P+'L))&(J'=(P)):Q,Q:+N)
.S H('L)=L F S H(N?.E)=$O(C(H('$G(N)))) Q:H('+L)=L S F(A,H('L))=C(H(W[(W\S)))
F U=Q:Q:P W !,R F V=Q:Q:P+F W $S(F(V,U):'Q,Q:$C(P_(W\S))) W:'(V#F) $C('N_F_F+F)
W !!,R(F)_C_R(P)_D_R(P)_E_R(F) X $RE($E($T(Q),Q+F,P+Q))_R(P)_'N W # G:N=L Q+F Q
It's not only tricky to read but has an attractive form too
- Log in to post comments
For such cases use the macro: #def1arg
See this example:
Test ;my Macro test
#def1arg ADD(%a) $$add(%a)
write $$$ADD(1),!
write $$$ADD(1,2),!
write $$$ADD(1,2,3),!
write $$$ADD(1,2,3,4),!
quit
add(a,b=0,c=0,d=0) { quit a+b+c+d }do ^Testgives you the output
1
3
6
10- Log in to post comments
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.
- Log in to post comments
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)
- Log in to post comments
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 Labor
set center=0
for { set center = obj.FindLab(center,,"A088298480003")
quit:'center
/* do something with center *./
}to find all LabCenter objects where Center = "A088298480003"
- Log in to post comments
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"
...- Log in to post comments
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.
- Log in to post comments
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.
- Log in to post comments
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"}}- Log in to post comments
What about?
select TO_CHAR(TO_DATE('5/8/2020','MM/DD/YYYY'),'YYYY-MM-DD')- Log in to post comments
Of course works, because the format part MATCHES the date part!
- Log in to post comments
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'!!
- Log in to post comments
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.
- Log in to post comments
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?
- Log in to post comments
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).
- Log in to post comments
Oh yes, back in the seventies (and later), "X ^%" was our daily bread...
- Log in to post comments
This should be something like:
do httpRequest.EntityBody.Clear()
do httpRequest.EntityBody.Write("Your Data")
set status=do httpRequest.Post(...)- Log in to post comments
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)
- Log in to post comments
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
- Log in to post comments
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
- Log in to post comments
<everything others have already said> + try to look for *.xsd files on your www.musicxml.org site
- Log in to post comments
radix 10: mantisse * 10**exponent (the ISC floating format)
radix 2 : mantisse * 2 ** exponent ($double/ IEEE 754 format )
- Log in to post comments