Vaccination History

I am trying to understand how to implement the OPV equivalent of the RxWorks vaccination certificate - this uses a mergefield called VaccinationRecordMemo - which from digging into the database runs a query to select from the patient's medical records, all the entries for a product of type Vacc*.

I can obviously build a report which extracts this information, but I really want to build a patient form or letter that creates the vaccination certificate. However, I am having difficulty understanding how to do this.

I can conceive of two possible approaches:

a) use a odt document and invoke a report macro to extract the vaccination history - problem - what context can I use in the report macro expression - party:getPatientVisit($patient) only accesses the last visit

b) use a jrxml document with a subreport to get the vaccination history - problem - what data source do I use for the sub-report.  Looking at the invoice jrxml, for the reminders sub-report the data source expression is:

$P{dataSource}.getExpressionDataSource("reminder:getReminders(., 1, 'YEARS')")

so there is evidently a mechanism to access information outside the base data source (which for invoices is act.customerAccountChargesInvoice). As scan of the source shows that this is a specific function reminders:getReminders(Party customer, int dueInterval, DateUnits dueUnits, boolean includeOverdue)

but I can't see a party:getHistory(Party patient, , , ,) function.

 

So:

Q1 - any suggestions

Q2 - how do Australian practices generate a vaccination certificate which shows all the vaccinations

Regards, Tim G

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Re: Vaccination History

I don't think there is a rich enough set of extension functions to do what you want.

At present, the only way to generate a vaccination certificate showing all vaccinations would be by developing an SQL report that is launched from Reporting|Reports.

The main limitations are:

a. no facility to query patient history. The only method at present is the party:getPatientVisit(patient) which returns the latest visit.

b. no facility to run SQL queries using the macro:eval() function.

Implementing extension functions for (a) is only useful for JasperReports.

Adding support for (b) gives the most flexibility, and the resulting text can be used in OpenOffice or Word templates. The main limitation is that you need to use a fixed width font.

Re: Vaccination History

Tim - thanks for the quick reply. I agree that an 'execute any sql query' would be the most flexible way to go.

Q1: do you envisage an upgrade of the current Report Macro with the Expression being a SQL query? If so do we worry about someone doing an INSERT rather than a SELECT - or is this a buyer beware problem - after all only Administrators can create the macros.  Its not as though we have an sql injection problem.

Q2: I had a play with macro:eval(@test) where the @test macro just returned multiple lines of text.  I can happily set the odt document to set any font for the returned text.  Hence I suspect that your mention of 'fixed width font' relates to the problem of being unable to inject a tab character to separate the columns of information. I had a play with the translate function to translate the | characters into tabs, ie [translate(macro:eval('@test'),'|','')] - but that didn't work - it translated the |'s to &'s - ie it did not recognise the  as a tab character.

If my analysis is correct, then the 'must use fixed width font' problem would go away if there was a method of getting the macro expander to inject a tab character (eg using say \t or ^J or  or whatever. Then one could write the sql to be like SELECT date, '\t', prodname .... and swriter would do the columns for you.

 

I suspect this is going to get to 'Tim G pays for yet another feature' ;-)

 

Regards, Tim G 

PS - I woke up this morning realising three things:

a)  I had not touched on the problem of passing parameters to the SQL query - my feeling is that provided that $patient.id and $customer.id are supported so that say:

 select DATE_FORMAT(p2.activity_start_time,'%e/%m/%Y'), IFNULL(ed.value,e2.name) from participations p
left join entities e on p.entity_id = e.entity_id
left join participations p2 on p2.act_id = p.act_id
left join entities e2 on p2.entity_id = e2.entity_id
left join entity_relationships er on er.target_id = e2.entity_id
left join entities e3 on e3.entity_id = er.source_id
left join entity_details ed on ed.entity_id = e2.entity_id
where p.entity_id = {$patient.id}
and p.act_arch_short_name = 'act.patientMedication'
and p2.arch_short_name = 'participation.product'
and er.arch_short_name = 'entityRelationship.productTypeProduct'
and e3.name like 'Vacc%'
and ed.name = 'printedName'
order by p2.activity_start_time; 

will return the vaccination history for the current patient, then we are in good shape. [Note that I have no idea as to whether the above query is the most efficient way to get the vacc history, but it works.]

b) if we are to use the Report Macro setup, then we need a special (perhaps null) entry in the Report field to indicate that the Expression is a SQL query, and that parameters (ie things between {}) should be parsed for and replaced before executing the query

c) there needs to be a definition as to how data is returned from the query. In the above I am returning two fields, the date and the product name. It would be neat if these could be simply returned with a tab character between them. Alternatively, if we invent a mechanism for encoding a tab within macro text, then the rule could be that the SQL query returns a single string, ie one should adjust the above to something like

 select CONCAT(DATE_FORMAT(p2.activity_start_time,'%e/%m/%Y'),'\\t', IFNULL(ed.value,e2.name)) from ... 

Regards, Tim G

Re: Vaccination History

Hi Tim,

Q1. The report macro supports generating a report from a template, so the change would be to support JasperReport templates that use SQL queries. These are already used in Reporting|Reports.

The challenge is how to pass appropriate parameters to SQL reports when launched from a macro.

Currently reports are passed a context object which is used to construct the data source. E.g.:

   macro:run("Discharge Summary", openvpms:get(., "patient.entity")) 

would launch a "Discharge Summary" report with a party.partientpet instance.

In Reporting|Reports, SQL reports are passed an SQL connection, and parameters are prompted for. For macros, parameter prompting cannot be supported.

The easiest approach would be to pass the context object via a predefined parameter name. E.g. "object".  You would refer to this in the report as $P{object}.

Q2. Report macros invoke JasperReports that generate text. All columns are space filled; tab is not used. So if you use a proportional font, columns won't align correctly. An alternative would be to also support HTML output generation.

a. See Q1 above. Hopefully you can get enough information from the $P{object} parameter. You can pass this to extension functions to get the other information. E.g. get the current owner of a patient.

b. Its possible to examine JasperReports to determine if they need an SQL connection or not.

c. The SQL queries would be executed by JasperReports and the formatted text returned, rather than SQL invoked directly.

 

-Tim A

Re: Vaccination History

OK Tim - we extend the current Report Macro facility to check if the specified report template is of type Report, if so run the report passing it the SQL connection, and parameter(s) as discussed below.

Parameter(s): when running a standard jrxml report via Reporting|Reports the code examines the report and extracts the parameter specifications, prompts for them and injects them in the appropriate format (ie as date or string or number).

In this case we cannot prompt and thus need an alternative method. In a standard report, each parameter is input as a string. Could we pass the parameters as a set of lines in the expression text. That is, if the report parameters were the patient id, the customer id, and a count, then the Expression

concat($patient.id,'
',$customer.id,'
','123')

will, when run through the standard macro expression evaluator, yield say

19654
1657
123

This is then split into three and the three strings inserted into the three parameters that would have been prompted for if the report was run via Reporting|Reports.

If the jrxl parameters order is always fixed (ie the first parameter is always the first one in the report parameter dialog) then we can just insert by order. If not, then we specify that the first line goes into Param1, then second into Param2, etc.

If would be nice if one could use a syntax something like

PatientId=$patient.id
CustId=$customer.id
Count=123

but I am certain that the 'expand the expression as a standard macro expression, then cut the resulting text by lines' approach will be a lot simpler to implement.  It is also relatively each to test - you create a standard macro and check that it expands as required; and you run the report via Reporting|Reports and check that the parameters it prompts for are as you expect and, with the data from the macro expansion, that the report generates the expected results.

Regards, Tim G

PS - the 'fixed width font' is still an annoyance - but I have realised that in my case (the Vacc history), it is not a problem because the first column contains the date, and that with all fonts, a date in dd/mm/yyyy format always has a fixed width. Also, in the case of say a medical history dump in a reference letter, it would not be awful if the history was in a different font - and there are fixed pitch fonts (eg QuickType Mono) that look way better than say Courier New.

Re: Vaccination History

It would be possible to add support for report macros that take a number of parameters; these would be defined as JXPath expressions in the report macro definition, and could define both the parameter name and parameter expression.

That said, I suspect it will be cheaper to just add extension functions to return the patient history. You would use these via the $P{dataSource}.getExpressionDataSource() method.

E.g.:

 $P{dataSource}.getExpressionDataSource("patient:visits(.)")

might return all visits for a patient, and:

$P{dataSource}.getExpressionDataSource("patient:medication(., 'VACCINATION')")

would return all patient medication with a VACCINATION product type.
 

There could also be functions that flatten the history, so you get:

  • Visit 1
  • Medication 1
  • Note 1
  • Visit 2
  • Note 2
  • Medication 2

instead of the hierarchical form which requires subreports for the visit items:

  • Visit 1
    • Medication 1
    • Note 1
  • Visit 2
    • Note 2
    • Medication 2

Using this approach, you would probably do your entire vaccination certificate as a JasperReport.

Re: Vaccination History

Tim A - I apologise for being tardy in responding (and I just got ping'ed by the practice manager asking 'what is happening about the vacc history problem').

Thus my short term goal would be met by a

$P{dataSource}.getExpressionDataSource("patient:medication(., 'VACCINATION')")

facility.

Can you please generate quote for the above.  Thanks.

----------------------------------------------------------------------

However, lot of flexibility would be provided by being able to use a subreport with an SQL connection. I played with the iReport sub-report settings and looked at the xml. Although in iReport you can set the connection type to either 'data source' or 'connection', in either case the xml is just <connectionExpression><![CDATA[xxx]]></connectionExpression> where xxx is the data source or connection expression.

In the current code, you obviously extract the expression and process it.

If we defined a special value "SQL", then I suspect it would be relative easy for you invoke the subreport with the appropriate SQL connection.

If the main report is given the patient entity, then subreport can be invoked passing the patient id as a parameter.

Regards, Tim G

Re: Vaccination History

I suspect you want to query by entity.productType rather than lookup.productGroup (i.e. via the medication's Type vs its Classifications), so the Type name would be used.

Is a method that just returns all medications for a patient by type name sufficient?

Possible methods:

  • history:getMedication(patient) - returns all medication acts for patient
  • history:getMedication(patient, from, to) - returns all medication acts for patient, dated between from and to
  • history:getMedication(patient, productTypeName) - returns all medication acts that have the specified product type
  • history:getMedication(patient, productTypeName, from, to) - returns all medication acts that have the specified product type, dated between from and to

Re: Vaccination History

Tim A - yes you are correct - it is by productType.

For my vacc history problem, history:getMedication(patient, productTypeName) is perfect. I understand that this is what RxWorks does, and the vets edit any old ones that they don't want.

For accessing the medical history, I supect that history:getMedication(patient, from, to) is the more useful one, because you can implement history:getMedication(patient) by history:getMedication(patient, "1900-01-01","2100-01-01").

In fact, the same is true of the productTypeName varient - ie I could do my vaccination certificate with
history:getMedication(patient,"VACCINATION","1900-01-01","2100-01-01").

Also, can you order by startTime descending - ie the newest ones at the top.

Thanks, Tim G

Re: Vaccination History

The date parsing support will reject those dates. Accepted values must be in one of the following formats:

  • "yyyy-MM-dd HH:mm:ss.S z"
  • "yyyy-MM-dd HH:mm:ss.S a"
  • "yyyy-MM-dd HH:mm:ssz", 
  • "yyyy-MM-dd HH:mm:ss z"
  • "yyyy-MM-dd HH:mm:ssa"

This is a requirement of org.openvpms.component.system.common.jxpath.UtilDateConverter. This would need to be changed, to allow support for dates of the form:

  • "yyyy-MM-dd"

The workaround is to use the java.sql.Date class to parse dates:

 history:medication(patient, 'Vaccination', java.sql.Date.valueOf('1900-01-01'), java.sql.Date.valueOf('2100-01-01'))

-Tim A

Re: Vaccination History

Re: Vaccination History

Tim A - I have just transferred the $100. Regards, Tim

Syndicate content