Eduard Lebedyuk · Dec 9, 2017 go to post

1. cls is used by several others languages there, it's okay.

2. We need more than 100 CLS repositories to add our highlighting to GitHub

3. GitHub language grammar == Atom language grammar. We have one, but it needs work.

Eduard Lebedyuk · Dec 8, 2017 go to post

System Management Portal -> Menu -> Databases -> HSAUDIT -> Mount RO (remove check) -> Save.

Eduard Lebedyuk · Dec 8, 2017 go to post

Here's the code I ran:

Class User.XMLTest
{

/// do ##class(User.XMLTest).Test()
ClassMethod Test()
{
    set source = ..GetXData("Source")
    set xslt = ..GetXData("XSLT")
    #dim result As %Stream.Object
    set sc = ##class(%XML.XSLT.Transformer).TransformStream(source, xslt, .result)
    
    do result.OutputToDevice()
}

ClassMethod GetXData(name) As %Stream.TmpCharacter [ CodeMode = expression ]
{
##class(%Dictionary.XDataDefinition).IDKEYOpen($classname(), name).Data
}

XData Source
{
Your Message
}

XData XSLT
{
Your XSLT
}

}

and here's the result:

{
"Message" :
{
"Header" :
{
"Code" : "HOT",
"Date" : "2017-12-08 11:22:34.658"
}, "Body" : [
 
{
"Code" : "HOT",
"Name" : "SIDE",
"Type" : "AADULT",
"Sec" : "MSec",
"ardge" : "Adult",
"Nder" : "F",
"TBC" : "21",
"BO" : "14",
"DBOC" : "0",
"LBC" : "5",
"AB" : "2"
},

...

Check %objlasterror and also check your XML encoding, there may be problems if your encoding does not match declaration.

Eduard Lebedyuk · Dec 7, 2017 go to post

So, for now, yes. I changed to article

It was a question? My bad, I thought I created an article.

Eduard Lebedyuk · Dec 7, 2017 go to post

But how about getting not just a progress but how to get some data before task finished. 

Append to data global as required and get data on each check, not only for complete requests?

And also, I see beg security Issue, when I can get information from any other process. You use TaskId directly from the request like you trust everybody. And also easy to get SUBSCRIPT error.

I completely agree that it's unsecure and with no $get(). But it's a minimal implementation, so I skipped a lot of details.

BTW: do you suppose to acept someone's answer? Looks like it is mostly just discissing topic.

I think it's more of a discussion topic. That's why it's an article and not a question.

Eduard Lebedyuk · Dec 7, 2017 go to post

You're getting an error:

:ERROR{ERROR #5002: Cache error: <PROPERTY DOES NOT EXIST>zXMLTOJson+6^TestEnvironment.Custom.GENERAL.CUSTOM.XLT.XMLToJsonOperation1.1 *Parameters,EnsLib.EDI.XML}

Which means :

  • Class is EnsLib.EDI.XML
  • It does not have Parameters property
  • Error is found in the 6th line of zXMLTOJson routine in TestEnvironment.Custom.GENERAL.CUSTOM.XLT.XMLToJsonOperation1.1

6th line is probably this:

 Set tSC = ..Transform(tInput,tSSKey,pRequest.Parameters,.tOutput)

and pRequest object (of EnsLib.EDI.XML class) does not have Parameters property. Your call should probably be:

 Set tSC = ..Transform(tInput,tSSKey,pRequest,.tOutput)
Eduard Lebedyuk · Dec 7, 2017 go to post

I thought about actually building the tree in ppg and recursively iterating over it till the error is pinpointed. But it sure would take a lot of time to implement. What's your approach? Any shortcuts?

Eduard Lebedyuk · Dec 6, 2017 go to post

I first translated each word into normalized form - array of [letter, number of occurrences], ordered by letters. And then just checked if given normalized form already exists:

ClassMethod IsLettersUnique(word) As %Boolean
{
    set length = $length(word)
    for i=1:1:length {
        set letter = $e(word, i)
        set temp(letter) = $i(temp(letter))
    }
    
    set normalized = ""
    set key = ""
    for {
        set key=$order(temp(key),1, occurrences)
        quit:key=""
        set normalized = normalized _ key _ occurrences
    }
    
    quit:$d(%normalized(normalized))>0 $$$NO
    
    set %normalized(normalized) = 1
    
    quit $$$YES
}

Normalized words:

  • aaba -> a3b1
  • aaab -> a3b1
Eduard Lebedyuk · Dec 6, 2017 go to post

Add:

Set email.Charset = "UTF-8"
Set email.IsHTML = $$$YES

and add text in html, for example:

Do email.TextData.Write($$$FormatText("Hello %1!<br>Thank you for visiting %2!", "Name", "Location"))

Also if your BO has only one method you can name in OnMessage and remove XData altogether.

If new lines is really the only thing you need formatting-wise, you can use WriteLine method and send plain text emails:

Do email.TextData.WriteLine($$$FormatText("Thank you for your recent visit to %1.", "Location"))
Do email.TextData.WriteLine("We continue to look for ways to improve and your feedback is important.")
Do email.TextData.WriteLine($$$FormatText("Please take a few moments to complete our survey by following the link provided below, or by completing the paper survey you may have received at discharge from your recent visit at %1.", "Location"))
Eduard Lebedyuk · Dec 3, 2017 go to post

Should've done Day 3 part 1 same as you. Deriving the formula took me like half an hour.

Eduard Lebedyuk · Dec 2, 2017 go to post

I'm in. Here's my solution.

Thought about using $zstrip, but while it can remove duplicates, it can't leave only duplicates:

w $zstrip("11223","=E")
123

 

Also, for second task it says:

1212 produces 6: the list contains 4 items, and all four digits match the digit 2 items ahead.
1221 produces 0, because every comparison is between a 1 and a 2.

Shouldn't 1221 produce 3 as first 2 and second 1 match.

Currently failed on second task with 10938.

Eduard Lebedyuk · Dec 1, 2017 go to post

Would skip classes with  several levels of inheritance from %Persistent. Use SubclassOf query instead.

Eduard Lebedyuk · Dec 1, 2017 go to post

URL map can also be splitted into several parts:

XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>

<!-- Api version 1 -->
<Map Prefix="/v1" Forward="Api.v1"/>

<!-- Api version 2 -->
<Map Prefix="/v2" Forward="Api.v2"/>

</Routes>
}

And brokers in Api.v1 and Api.v2 would process the requests according to their URL Map.

Eduard Lebedyuk · Nov 30, 2017 go to post

Complex functional indices which take a long time to build my be worth moving, otherwise - no.

Eduard Lebedyuk · Nov 30, 2017 go to post

cpf files.

All classes from Config package affect one of cpf file (active one by default). But you also need to save and activate modified config file. Dmitry solution does that all by default. Which is fine if you want to add one mapping, but if you want to modify several Config settings it's better to modify them and activate the changes separately. For example here's mapping several packages to %ALL.

#include %syConfig
set namespace = "%ALL"
set namespace = $zcvt(namespace, "U")
for package = "Package1","Package2","Package3" {
    kill p
    set p("Database")=pFrom
    set sc = ##Class(Config.MapPackages).Create(namespace, package,.p,,$$$CPFSave)
}
set sc = ##Class(Config.CPF).Write()
set sc = ##Class(Config.Map).MoveToActive(namespace)
set sc = ##Class(Config.Namespaces).Load(namespace)
Eduard Lebedyuk · Nov 30, 2017 go to post

Note: if you want to add a large number of mappings, you should first save all new mappings and then activate the new config file afterwards.

This is applicable to all Config modifications.

Eduard Lebedyuk · Nov 29, 2017 go to post

You can add Ens.ProductionMonitorService service to your production, which would provides a monitor service for the production status The default behavior of this service is to call UpdateProduction once it notices the production is not up-to-date. Users can override the OnProcessInput method to customize the behavior.

It would be enough to automatically restart relevant items and keep production up to date.

Generally to access/manipulate info about Ensemble and it's hosts use Ens.Director class.

Eduard Lebedyuk · Nov 29, 2017 go to post

Have you registered your Callback class with Health Monitor ( Configure Callback option in the Set Health Monitor Options submenu of the ^%SYSMONMGR utility)?

Eduard Lebedyuk · Nov 28, 2017 go to post

I usually use this proxy operation that keeps track of token:

Class Production.AuthOperation Extends Ens.BusinessOperation
{

Parameter ADAPTER = "EnsLib.HTTP.OutboundAdapter";

Property Adapter As EnsLib.HTTP.OutboundAdapter;

/// Token
Property Key As %String;

/// Number of seconds, that the key is valid
Property KeyValid As %Integer [ InitialExpression = 7200 ];

/// Time, till which the key is valid
Property ValidUntil As %TimeStamp;

Parameter INVOCATION = "Queue";

Parameter SETTINGS = "KeyValid:Basic";

/// Get token
Method OnMessage(request As Ens.Request, Output response As Ens.StringResponse) As %Status
{
    #dim sc As %Statis = $$$OK
    set now = $horolog
    if $System.SQL.DATEDIFF("second", now, ..ValidUntil)<=0 {
        // Get new key valid period
        set validUntil = $System.SQL.DATEADD("second", ..KeyValid, now)
        
        // Get key from auth server by sending user/pass
        set input = { "user": (..Adapter.%CredentialsObj.Username), "password": (..Adapter.%CredentialsObj.Password)}
        set sc = ..Adapter.Post(.httpResponse,,input)
        quit:$$$ISERR(sc) sc
        
        // Parse key from response
        set ..Key = {}.%FromJSON(httpResponse.Data).key
        set ..ValidUntil = validUntil
        
    }
    set response = ##class(Ens.StringResponse).%New(..Key)
    quit sc
}

}
Eduard Lebedyuk · Nov 27, 2017 go to post

What do you mean by overhead from $lb? 

I mean overhead from

  • Converting string to/from $lb
  • Converting each char to/from number

 

What is $c(166), some magic number?

Essentially. 166(dec) = 10100110(bin), where 101 means  fixstr datatype (stores a byte array whose length is upto 31 bytes) and 00110(dec) = 6(dec) which is the length of the string.

And why you encode the string as a list of codes, why not keep it as a string?

@Maks Atygaev as an advocate of that approach can clarify his position.

Eduard Lebedyuk · Nov 27, 2017 go to post

To elaborate a bit: we are currently discussing two approaches on how to store intermediary representations of Messagepack message.

One of approaches is to store message as $lb(code1, code2, ... ,codeN) so that each character in message is stored as a corresponding code in $lb.

Other approach is to store as is in strings, so it would look like: $c(166) _ "Fringe".

Let's say we want to encode string "Fringe", it would look like this:

  • $listbuild(166, 70, 114, 105, 110, 103, 101)
  • $c(166) _ "Fringe"

 

I maintain that approach ASIS (with $c) is better as it gives less overhead than $lb.

There is no option to directly read 166, 70, 114... in Caché, only reading symbols and getting their codes from $ascii. So there would be a considerable overhead on conversion to and from $lb abstraction.