When attempting to use ca.uhn.hl7v2.util.Terser to set empty fields on a given specific subclass of ca.uhn.hl7v2.model.Message (in this case ca.uhn.hl7v2.model.v251.message.ORU_R01), I receive no error messages during the .each{} closure and afterwards the message object has no fields populated.
hl7Map is populated on class instantiation, with values like:
def hl7Map= [ "HL7MessageFields":['PID-3-1':"internal order map key",
'PID-3-4':"internal order map key",etc.]]
Code below:
def buildHL7Message(order){
def date = new Date()
def format = new SimpleDateFormat(hl7Map["dateFormat"]).format(date)
//Set date on the Message Header Object
hl7Map["MSH"]["-7"]= format
def message = (context.getModelClassFactory().getMessageClass(hl7Map["MessageInstantiationMap"]["messageType"],
hl7Map["MessageInstantiationMap"]["version"],
true) as Class).newInstance()
Terser terser = new Terser(message)
hl7Map["HL7MessageFields"].each{
terser.set(it.key, order[it.value])
}
println message
return message
}
The end of method results in no output and error logged about encoding, MSH-1 is a required field, pipe delminator but is empty. If the code above uses message.initQuickstart("ORU", "R01", "T"), only the default initQuickstart fields are populated.
If hl7Map["HL7MessageFields"] contains a 'it.key' that is not a valid Group/Segment field, an error is logged by terser that it failed to find the value, the above code with a properly formatted map does not cause an error.
Can anyone help explain why I am not receiving errors yet my message is empty, and help me to populate the message with the appropriate terser.set(params)?
Found solution that worked for me after a couple of hours of searching.
The message object's internal representation has a tree like structure where the MSH Segment is the parent, and the segments located after the MSH segment are child segments. Because of this structuring, MSH fields must be set as my original code does, but child segment fields must be set with "/." prepended to the map key devised (i.e. "PID-3-1" must become "/.PID-3-1" in the terser.set() line.
The hl7Map format was updated to better support this terser.set() syntactical requirement.
From the terser documentation, the / indicates search should start at the root of the message, and from an answer on a HAPI mail list link I've now lost, the . indicates search should include child elements of the MSH.
Full code below:
def buildHL7Message(order){
def date = new Date()
def format = new SimpleDateFormat(hl7Map["dateFormat"]).format(date)
//Set date on the Message Header Object
hl7Map["MSH"]["-7"]= format
//See http://stackoverflow.com/questions/576955/groovy-way-to-dynamically-invoke-a-static-method
//And
//http://stackoverflow.com/questions/7758398/groovy-way-to-dynamically-instantiate-a-class-from-string
def message = (context.getModelClassFactory().getMessageClass(hl7Map["MessageInstantiationMap"]["messageType"],
hl7Map["MessageInstantiationMap"]["version"],
true) as Class).newInstance()
Terser terser = new Terser(message)
hl7Map["MSH"].each{
terser.set("MSH"+it.key, it.value)
}
hl7Map["HL7MSHChildSegmentMap"].each{
terser.set(("/."+it.key) as String, order[it.value] as String)
}
println message
return message
}
Related
I'm currently trying to manipulate a map in groovy but I'm facing a problem that I can't work out.
I build a map in order to have an id as key and a name as value
I have to store it as a string, then recover it and rebuild the map.
My keys look like id:my:device, names look like
When I build my map, I end up having something like
mymap = [id:my:device: ...etc.] which does not cause any problem for recovery, mymap[id:my:device] gives my device name.
EDIT :
I build the map doing name_uid_map[measure.uid] =jSonResponse.value for every map element and, at the end of my testCase, I store it doing testRunner.testCase.setPropertyValue("name_uid_map", name_uid_map.toString()
After storage and recovery, as it is stored as a string, it becomes uneasy to decypher. I modify the string in order to have "id:my:device"='my device name', then I rebuild the map doing the following (otherwise it splits from the first ':')
mymap = map.split(",\\s*").collectEntries{
def keyAndVal = it.split("=")
[(keyAndVal[0]):keyAndVal[1]]
}
The problem is now my rebuilt map looks like
{"id:my:device"='my device name' ... }
If I do
mymap.each{
key, value ->
log.info key
log.info value
}
I obtain
key : "id:my:device"
value : my device name
which is correct. When I want to recover value from the key, I encounter my problem, ie:
mymap["id:my:device"] = null
If I try to get the type of the value I get :
my value = class org.codehaus.groovy.runtime.NullObject
I'm not easy at all with handling maps in groovy and I'm sure I've done something wrong, but I can't figure it out, could someone help me ?
Alex
Well,
Actually I found another way to fulfill my needs
In the testStep where I build my initial map I do the following:
import groovy.json.JsonBuilder
and I store my map in a custom property like this, to make sure it's a valid JSON
testRunner.testCase.setPropertyValue("name_uid_map", new JsonBuilder(name_uid_map).toString())
In the next testStep, I do the following (simple extraction of JSON):
def name_uid_map = context.expand( '${#TestCase#name_uid_map}' )
def jsonSlurper = new groovy.json.JsonSlurper()
map = jsonSlurper.parseText(name_uid_map)
and it works fine.
This is my structure:
class Imprint_Location(models.Model):
_name = 'imprint.location'
name = fields.Char()
product_id = fields.Many2one('product.template')
class Imprint_Charges(models.Model):
_name = 'imprint.charge'
_rec_name = 'location_id'
product_id_c = fields.Many2one('product.template', required=True)
location_id = fields.Many2one('imprint.location', required=True)
#api.multi
#api.onchange('product_id_c', 'location_id')
def product_filter(self):
res = {}
print '\n\n-------\n\n', self, self.product_id_c, '\n\n-------\n\n'
if self.product_id_c:
res['domain'] = {'location_id': [('product_id', '=', self.product_id_c.id)]}
print res
return res
class Product_Template(models.Model):
_inherit = 'product.template'
imprint_location_ids = fields.One2many('imprint.location', 'product_id')
sale_imprint_charge_ids = fields.One2many('imprint.charge', 'product_id_c')
Now i have defined a page in product.template and inside the page is sale_imprint_charge_ids which is in <tree editable="bottom"> and i am not selecting the product_id_c field[also this field doesn't show up in the tree defined].
Now my problem here is that when i select this from the form view which i defined for imprint.charge the method product_filter works fine, but when i enter from the product.template then i get a error saying
TypeError: <odoo.models.NewId object at 0x7fbb8bc21b90> is not JSON serializable
Because from product.template if passes the object <odoo.models.NewId object at 0x7fbb8bc21b90> , so if print self.product_id_c then it prints product.template(<odoo.models.NewId object at 0x7fbb8bc21b90>) so this is not serializable. i have tried doing self.product_id_c.ids which give output empty list [].
So how do get the product.template id from the object or pass the id itself overriding some method.
You should improve couple of following points.
res['domain'] = {'location_id': [('product_id', '=', self.product_id_c.id)]}
return res
study some search() method of ORM
Try with following code:
#api.multi
#api.onchange('product_id_c', 'location_id')
def product_filter(self):
res = {}
if self.product_id_c:
self.location_id = False
#search product_template in imprint.locationwith table and limit we will get only record if related record found
location_id = self.env['imprint.location'].search([('product_id', '=', self.product_id_c.id)], limit=1)
if location_id:
#location_id.ids will give you something like [2] so we need to set value as 2
self.location_id = location_id.ids[0]
EDIT:
As per your first comment, you need a list of related location then we should following trick.
Remove product_filter() method
Add domain in imprint.charge object view file
For example:
<field name="location_id" domain="[('product_id', '=', product_id_c)]"/>
Afterwards, Restart Odoo server and upgrade your custom module.
When creating a brand new record Odoo creates that wierd <odoo.models.NewId object at 0x7fbb8bc21b90> object. After you have written the record this id gets turned into the normal ids that you are used to (an integer). In this situation you have a function which (not unreasonably) expects a real id value at a point when no such value really exists. You need to provide a fallback, such as evaluating if the id is an integer and providing an alternate value in that circumstance. Although your function seems to return an object which I dont quite know what you are expecting to happen. If you wish to modify the value of one of your fields I would modify the values of the self object rather that returning an object.
in soapui my project is :
Project
|__Datasource
|__request
|__groovy_code
|__DatasourceLoop
My Datasource contains 100 lines, each one is a request with different parameters.
My groovy_code save the id from the response of the request.
When i run my project, it executes 100 requests without errors.
groovy_code save only the first id.
What i want is to save id for each request, so 100 ids in different variables at project level
Here is my groovy_code:
import groovy.json.JsonSlurper
def response = context.expand( '${login#Response#declare namespace ns1=\'https://elsian/ns/20110518\'; //ns1:login_resp[1]/ns1:item[1]/ns1:response[1]}' )
def slurper = new JsonSlurper()
def result = slurper.parseText(response)
log.info result.data.id
testRunner.testCase.testSuite.project.setPropertyValue("token_id", result.data.id)
Thank you for your help
I never use SOAPUI PRO and I don't have access to datasource testStep or even datasource loop.
However based on the project structure you're showing I suppose that for each time datasource loop founds a element in datasource it sends the flow to request step so request and groovy steps are executed on each iteration; due to this I think that the problem is that your groovy code is overriding each time the same property with a new value.
Then to solve this you can try adding some variable suffix to your property name to avoid override each time the property value. For example you can add to token_id string a counter, some uuid, current ms etc.
For example you can use a counter as a suffix. To keep the counter value you've to save it in the context variable, this way this property are shared between your tests inside the current execution:
import groovy.json.JsonSlurper
// create a suffix function to generate
// the suffixs for your property names based on a count
def getSuffixNameProperty = {
// check if already exists
if(context['count']){
// if exists simply add 1
context['count']++
}else{
// if not exists initialize the counter
context['count'] = 1
}
return context['count']
}
def propertyName = "token_id" + getSuffixNameProperty();
def response = context.expand( '${login#Response#declare namespace ns1=\'https://elsian/ns/20110518\'; //ns1:login_resp[1]/ns1:item[1]/ns1:response[1]}' )
def slurper = new JsonSlurper()
def result = slurper.parseText(response)
testRunner.testCase.testSuite.project.setPropertyValue(propertyName, result.data.id)
I've this code by which I am trying to set the value of a Key (a json node id). But its not setting the value. Log.info statement is showing right values.
Key= context.expand( '${#Project#key}' )
Value= context.expand( '${#Project#value}' )
Binding binding = new Binding()
binding.setVariable("v", "$Value")
binding.setVariable("k", "$Key")
log.info(binding.getVariable("v")) // gives me the value 1234
log.info(binding.getVariable("k")) // gives me the value request.id
def SetKey = new GroovyShell(binding).evaluate( "k=v")
Can someone please comment on whats wrong in this code. and how can I correct it.
Edit: Explanation of the issue
In SoapUI I've some json nodes saved in data source like this request.id and request.app.id and there expected values in Value column which I am fetching through Key and Value above. I am hoping to change the value of a json node to its respective value at run time. So for each iteration of data source, it should set the correct value of that particular json node. Befor the above code I've parsed my json request by json slurper and after the above code I am building the json again by Json builder and running the request. Parsing and running the request works fine, its just that I couldnt set the value of the json node.
If your keys are just dotted names, you could simply use some string manipulation, no need to involve the Groovy parser.
For example, if they all begin with request:
def steps = Key.split(/\./)
if (steps.size() < 2 || steps[0] != 'request') {
throw ...
}
def obj = request
if (steps.size() > 2) {
steps[1..-2].each {
obj = obj[it]
}
}
obj[steps[-1]] = Value
I'm just getting started with Groovy/Grails
I noticed the error messages you get when you validate a form look like this:
Property [{0}] of class [{1}] cannot be blank
For example this code to dump the errors to the console
s.errors.allErrors.each
{
println it.defaultMessage
}
Now, it.arguments contains the arguments that need to be filled in here.
The problem is, I can't find any method in the Grails or Groovy documentation that formats strings based on positional parameters like {0}, {1} and substitutes values from an array
I need something like python's %
What is the proper way to format these error strings so the parameters get substituted properly?
These markers are actually replaced using the standard java.text.MessageFormat APIs. If you display the messages using Grail's g:message tag, it will fill in the gaps if you pass a suitable args="..." attribute:
<g:message code="mymessagecode" args="${['size', 'org.example.Something']}"/>
Under certain circumstances (within GSP pages and from controllers IIRC) you cann call the tag like a function:
g.message(code:'mymessagecode',args: ['size', 'org.example.Something'])
Note, that the value to supply as message code is only a symbolic string constant. The actual translation (the message text with the "gaps" in it) will be read by the framework using Spring's reloadable resource bundles.
If all you actually have is a translation text, you can call the message formatting APIs directly. See for example:
import java.text.MessageFormat
...
args = ["english"].toArray()
println(MessageFormat.format("Translation into {0}", args))
// Or - as the method is variadic:
println(MessageFormat.format("Translation into {0}", "english"))
Look what Groovy can do for you, using a little bit of meta-programming.
MessagesBundle_en_US.properties:
greetings = Hello {0}.
inquiry = {0}: How are you {1}?
farewell = Goodbye.
ResourceBundleWithSugar.groovy:
import java.text.MessageFormat
class ResourceBundleUtils {
def propertyMissing(String name) { this.getString(name) }
def methodMissing(String name, args) {
MessageFormat.format(this.getString(name), args)
}
}
ResourceBundle.metaClass.mixin ResourceBundleUtils
def msg = ResourceBundle.getBundle("MessagesBundle", new Locale("en","US"));
println msg.greetings("Serge")
println msg.inquiry("Serge","Mary")
println msg.farewell // You can use also: msg.['farewell'] msg."farewell" or msg.getString("farewell")
Output:
Hello Serge.
Serge: How are you Mary?
Goodbye.