Why don't you want to do it in the rule editor?
- Log in to post comments
Why don't you want to do it in the rule editor?
Same as with modifying a rule:
Business Rule Definition is stored as XML in XData block named RuleDefinition inside rule class and can be (de)serialized as an object of Ens.Rule.Model.ruleDefinition class. Some utility methods are available there.
Here's an example of modifying Demo.ComplexMap.Rule.SemesterBatchRouting rule in ENSDEMO class. It modifies "when" condition from 1 to 0.
zn "ENSDEMO" set ruleClass = "Demo.ComplexMap.Rule.SemesterBatchRouting" set sc = ##class(Ens.Rule.Model.ruleDefinition).LoadFromClass(ruleClass, .rule) set rule.ruleSets.GetAt(1).actions.GetAt(1).whens.GetAt(1).condition=0 w rule.SaveAs(ruleClass) set sc=$system.OBJ.Compile(ruleClass,"k-d")
But instead of:
set sc = ##class(Ens.Rule.Model.ruleDefinition).LoadFromClass(ruleClass, .rule)
You need to create and fill rule object manually.
Your first link leads to class documentation root.
In any place (but preferably in the beginning) of your REST handler method add these lines:
set %response.ContentType = "text/html" do ##class(%CSP.Utils).DisplayAllObjects() quit $$$OK
and then open REST Url to see all current objects, including %request.
You can use %Dictionary package to modify classes.
For example you can open property which is an object of %Dictionary.PropertyDefinition, modify it and save. Recompile the class to see new class definition.
Here's an example of %Dictionary modification to create new method.
Code for the custom adapter that specifies Content type:
Class Production.Adapter.HTTPOutboundAdapter Extends EnsLib.HTTP.OutboundAdapter
{
Method Post(Output pHttpResponse As %Net.HttpResponse, pFormVarNames As %String, pData...) As %Status
{
quit ..SendFormDataArray(.pHttpResponse, "POST", ..GetRequest(), .pFormVarNames, .pData)
}
ClassMethod GetRequest() As %Net.HttpRequest
{
set request = ##class(%Net.HttpRequest).%New()
set request.ContentType = "application/json"
quit request
}
}Note that this way only one-line method Post gets redefined (you'll also need to redefine Get method, but still - these are small one-line methods). ContentType may be further refactored as a setting.
Some comments
Business Rule Definition is stored as XML in XData block named RuleDefinition inside rule class and can be (de)serialized as an object of Ens.Rule.Model.ruleDefinition class. Some utility methods are available there.
Here's an example of modifying Demo.ComplexMap.Rule.SemesterBatchRouting rule in ENSDEMO class. It modifies "when" condition from 1 to 0.
zn "ENSDEMO" set ruleClass = "Demo.ComplexMap.Rule.SemesterBatchRouting" set sc = ##class(Ens.Rule.Model.ruleDefinition).LoadFromClass(ruleClass, .rule) set rule.ruleSets.GetAt(1).actions.GetAt(1).whens.GetAt(1).condition=0 w rule.SaveAs(ruleClass) set sc=$system.OBJ.Compile(ruleClass,"k-d")
That said, I think the better approach would be to use (in order of
increasing implementation difficulty):
So the rule by itself does not change but values supplied by Context Class/Temporary Variables/etc do.
You can decode JSON escaped characters:
set string = $zcvt(string, "I", "JSON")
and remove special symbols after that.
You can get output from %ToJSON() into a variable without any redirection:
set jsonString = dynamicObj.%ToJSON()
iKnow or iFind?
If iFind, then what index?
It depends on your corpus, and diversity of concepts encountered there.
I have 1:1 as corpus size:index size as a baseline, but it all depends on many factors.
Thank you, Alexander.
Here's an article on useful autogenerated methods for properties, indices, queries, etc.
Here's a write-up on defining your own query types to add more methods than just Func().
This condition:
Super [ 'Persistent'
Is insufficient. Consider this case:
Class Package.ClassA Extends %Library.Persistent
{
}
Class Package.ClassB Extends %XML.Adaptor
{
}
Class Package.ClassC Extends (ClassA, ClassB)
{
}While Package.ClassC satisfies both conditions (it's a subclass of both %Library.Persistent and %XML.Adaptor), it would not be returned by the SQL query, as Super field does not contain required superclasses directly.
But we can easily join 2 SubclassOf queries via SQL:
SELECT s1.name
FROM %Dictionary.ClassDefinitionQuery_SubclassOf('%Library.Persistent') s1
INNER JOIN %Dictionary.ClassDefinitionQuery_SubclassOf('%XML.Adaptor') s2 ON s2.name = s1.nameDoes it work like this?
ClassMethod WriteCapture(vstrCommand As %String) As %String
{
set tOldIORedirected = ##class(%Device).ReDirectIO()
set tOldMnemonic = ##class(%Device).GetMnemonicRoutine()
set tOldIO = $io
try {
set str=""
//Redirect IO to the current routine - makes use of the labels defined below
use $io::("^"_$ZNAME)
//Enable redirection
do ##class(%Device).ReDirectIO(1)
XECUTE (vstrCommand)
} catch ex {
set str = ""
}
//Return to original redirection/mnemonic routine settings
if (tOldMnemonic '= "") {
use tOldIO::("^"_tOldMnemonic)
} else {
use tOldIO
}
do ##class(%Device).ReDirectIO(tOldIORedirected)
quit str
//Labels that allow for IO redirection
//Read Character - we don't care about reading
rchr(c) quit
//Read a string - we don't care about reading
rstr(sz,to) quit
//Write a character - call the output label
wchr(s) do output($char(s)) quit
//Write a form feed - call the output label
wff() do output($char(12)) quit
//Write a newline - call the output label
wnl() do output($char(13,10)) quit
//Write a string - call the output label
wstr(s) do output(s) quit
//Write a tab - call the output label
wtab(s) do output($char(9)) quit
//Output label - this is where you would handle what you actually want to do.
// in our case, we want to write to str
output(s) set str=str_s quit
}You can use simply:
..RetryInterval
Instead of
%Ensemble("%Process").RetryIntervalSince current object is %Ensemble("%Process").
set obj = {"RecordIDs":[{"ID":"1234","Type":"INTERNAL"},{"ID":"1234","Type":"EXTERNAL"}],"ContactIDs":null,"Items":[{"ItemNumber":"320","Value":null,"Lines":[{"LineNumber":0,"Value":"1","Sublines":null},{"LineNumber":1,"Value":"100063287","Sublines":null}]}]}
w obj.Items.%Get(0).Lines.%Get(0).LineNumber
>0
w obj.Items.%Get(0).Lines.%Get(1).LineNumber
>1To iterate over arbitrary number of array elements use iterator:
set iterator = obj.Items.%Get(0).Lines.%GetIterator()
while iterator.%GetNext(.key,.line) { w line.LineNumber,! }
>0
>1It's a global that stores information about processed files. It can be accessed via $$$DoneFileTable macro - it's defined in EnsLib.File.InboundAdapter.
You'll need to subclass EnsLib.File.InboundAdapter and modify this line in OnTask method to do what you need:
$$$LOGINFO("Skipping previously processed file '"_tOneFilename_"'")
Alternatively you can monitor done file table and send alerts when you find new records.
Both options seem lacking.
Provided your patient MRN doesn't contain whitespaces, you can use $justify to pad string to required length and $translate to convert whitespaces into zeros.
w $tr($j("a", 10), " ", 0)
>000000000aMaybe you're sending HTML emails?
WriteLine works only for plaintext emails.
Use <br/> for new line in HTML emails.
To send HTML emails specify these settings:
set mail.ContentType = "text/html" set mail.IsHTML = 1 set mail.Charset = "utf-8"
ID value contains all pieces of the primary key separated by ||. In the case of %Dictionary.StorageSQLMapDefinition it's:
Class||Storage||SQLMapName
So to open %Dictionary.StorageSQLMapDefinition you need to either construct an ID like this:
Set Class = "%CSP.Util.Performance" Set Storage = "CspPerformance" Set SQLMapName = "Map1" Set Id = $LTS($LB(Class, Storage, SQLMapName), "||") Set Obj = ##class(%Dictionary.StorageSQLMapDefinition).%OpenId(Id)
Alternatively you can use Open method for IDKEY index (more on auto-generated methods for indices, properties etc.):
Set Obj = ##class(%Dictionary.StorageSQLMapDefinition).IDKEYOpen(Class, Storage, SQLMapName)
There are several ways to handle this:
Code sample for 1 - utility method to search for setting in a production object.
/// Find BH in a current production by a setting value
ClassMethod findConfigItemBySettingValue(settingName As %String, settingValue As %String, businessType As %String = "", enabledOnly As %Boolean = 0) As Ens.Config.Item
{
#dim sc As %Status
// Get current production object
#dim prod As Ens.Config.Production = ..getCurrentProduction()
if '$isObject(prod) quit ""
// Cycle over production elements
#dim item As Ens.Config.Item
#dim result As Ens.Config.Item = ""
for i = prod.Items.Count():-1:1
{
set item = prod.Items.GetAt(i)
// Search only for specified type
if ((businessType '= "") && (item.BusinessType() '= businessType)) || (enabledOnly && 'item.Enabled) continue
// Cycle over settings
do item.PopulateModifiedSettings()
set ind = ""
for
{
set setting = item.ModifiedSettings.GetNext(.ind)
if (ind = "") quit
// Found it?
if (setting.Name = settingName)
{
if ($ZStrip(setting.Value, "<>W") = $ZStrip(settingValue, "<>W")) set result = item
quit
}
}
if $isObject(result) quit
}
quit result
}
/// Get current running production object
ClassMethod getCurrentProduction() As Ens.Config.Production
{
#dim sc As %Status
#dim prodName As %String
#dim prodState As %Integer
// Find the name and status of current production
set sc = ##class(Ens.Director).GetProductionStatus(.prodName, .prodState)
if $$$ISERR(sc)
{
$$$LOGERROR($System.Status.GetErrorText(sc))
quit ""
}
//Status should be "Running"
if (prodState '= $$$eProductionStateRunning) quit ""
// Open object by name
#dim prod As Ens.Config.Production = ##class(Ens.Config.Production).%OpenId(prodName, , .sc)
if $$$ISERR(sc)
{
$$$LOGERROR($System.Status.GetErrorText(sc))
quit ""
}
quit prod
}
Check out SystemMethodsRemover - it does automatic regexp replace on the codebase. It's configured for system methods replacing, but you can easily repurpose it.
Why return binary data in JSON? I think it preferable to make a separate request for it.
SQL join or is there something more elaborate?
SELECT cd.name FROM %Dictionary.ClassDefinition cd JOIN subclassofquery sq ON sq.Name = cd.Name WHERE cd.System IS NULL
Let's say you have this production definition:
Class Passthrough.Production Extends Ens.Production
{
XData ProductionDefinition
{
<Production Name="TEST" LogGeneralTraceEvents="false">
<Description></Description>
<ActorPoolSize>2</ActorPoolSize>
<Item Name="PassthroughOperation" Category="" ClassName="EnsLib.SOAP.GenericOperation" PoolSize="1" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
<Setting Target="Adapter" Name="HTTPServer">www.webservicex.net</Setting>
<Setting Target="Adapter" Name="URL">|</Setting>
</Item>
<Item Name="PassthroughService" Category="" ClassName="Passthrough.PassthroughService" PoolSize="0" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
<Setting Target="Host" Name="TargetConfigName">PassthroughProcess</Setting>
<Setting Target="Adapter" Name="EnableStandardRequests">1</Setting>
<Setting Target="Adapter" Name="Port"></Setting>
<Setting Target="Host" Name="OneWay">1</Setting>
</Item>
<Item Name="PassthroughProcess" Category="" ClassName="Passthrough.PassthroughProcess" PoolSize="1" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
<Setting Target="Host" Name="TargetConfigName">PassthroughOperation</Setting>
</Item>
</Production>
}You can change
After recompile, new name should be displayed in SMP.