Darius, i can't write down everything we'd need in 5 min, its bigger than that.
And before I start, let me just say that i'm game to put a few days of
coding into this over the next week, because i have a strong incentive
to see this through -- this would allow me to retire
reportingobjectgroup, as well as solve Bob's problems. Also @Bob, i'm
based in Rwanda, and if you're here in a couple of weeks, i'd be glad
to meet up for an afternoon and try to get the framework to spit out a
few of your sql indicators.
Ok, here goes:
First, this is all independent of dimensions, although there's no
reason we couldn't build a DataSetColumnIndicatorAndDimensionResult
class to handle them later on.
And, note that we should create a new interface for DataSetDefinitions
-- one would ensure that there's a getColumn(String name) which would
make it ok to use a given DataSetDefinition implementation in a
DataSetColumnIndicator. This new interface could also include an
intersectWithCohortOnColumn(String colName) method. I haven't gone
through all the DataSetDefinition implementations to determine which
would be appropriate vs not, but this would be a necessary part of
this work.
Here are the new objects (i haven't thought this through enough to
define every method, but the basic class properties are a bit clearer
to me):
Also, I don't know if its better to look up a DataSet column by name
or by index. Does anyone have an opinion on this? Or should we
support both?
-----------------
DataSetColumnIndicator extends BaseIndicator
properties:
dataSetDefinition (this type of this DataSetDefinition would be the
DataSetDefinition interface proposed above)
columnName (or index?)
aggregator (default SingleValueAggregator?)
(for fraction indicators, optional):
denominatorDataSetDefinition
denominatorColumnName
denominatorAggregator
(optional)
locationFilter? (per CohortIndicator)
----------------
SingleValueAggregator
compute() method would validate that a dataset is 1X1 and return the
value in the DataSet.
(Note, we could also imply SINGLE_VALUE by just leaving aggregator
null for a given column indicator, and handle the 1X1 DataSet size
validation in the evaluator.)
(we could also transform % indicators in this class, potentially from
decimal to percent)
---------------
SqlIndicator extends DataSetColumnIndicator
setSqlQuery(String sqlQuery) -- should build a SqlDataSetDefinition
with sql query, aggregator of SINGLE_VALUE, register default column
name or index for 1X1 table
--------------
@Handler(supports={SqlIndicator.class,DataSetColumnIndicator.class})
DataSetColumnIndicatorEvaluator implements IndicatorEvaluator
methods: evaluate(): handles aggregations, numerator and denominator
This is where all parameters are passed to DataSetDefinitions, Filters, etc...
----------------
DataSetColumnIndicatorResult implements IndicatorResult
properties: DataSetColumnIndicator
EvaluationContext
Cohort (optional; the patients in the numerator, if
there's a mechanism for pulling out patients who contributed to the
indicator?)
(note, we might want to implement a new interface for
IndicatorResults, if we want to make the indicator a live link to the
underlying Cohort in the web renderer).
I'll going to try to call in today. I hope this helps.
d
On 8/23/11, Darius Jazayeri <[email protected]> wrote:
> Dave,
>
> Could you spend 5 minutes before tomorrow morning (9am Eastern) identifying
> exactly which files and methods would need to be touched/added to implement
> the DataSetColumnIndicator as you've described it? That would really help
> determine whether or not it's feasible for the HISP India team to implement
> this as a solution to their current problem. (And regardless, I can put that
> breakdown into tickets and sub-tickets for the next reporting sprint.)
>
> -Darius
>
> On Tue, Aug 23, 2011 at 9:45 AM, Dave Thomas <[email protected]> wrote:
>
>> Hey mike. Yes, I think this could replace reportingobjectgroup.
>> objectgroups are just 2-by-N tables.
>>
>> I'm also thinking that we could put in something reasonable for
>> intersecting with cohorts. Something like an intersectWithCohortOn(String
>> columnName) as an optional property on datasets. This property would
>> point
>> to the column with patient_ids.
>>
>> we could even use this intersection property to build an optional cohort
>> associated with any indicator result, so that you'll be able to use the
>> cohort indicator result UI without any big changes...
>>
>> Michael Seaton <[email protected]> wrote:
>>
>> >Dave,
>> >
>> >Thanks for synthesizing these ideas so well. This sounds like just what
>> >I had envisioned in previous discussions and would like to get onto the
>> >0.7 Road Map for Reporting. Do you envision this as providing any
>> >replacement functionality for the report object group stuff, or are
>> >these too separate?
>> >
>> >Cheers,
>> >Mike
>> >
>> >
>> >On 08/23/2011 10:14 AM, Dave Thomas wrote:
>> >> Hi. I've been looking at the reporting code today, and here's what
>> >> i'm thinking about two new implementations of Indicator:
>> >>
>> >> Here's what i'd really like: a DataSetColumnIndicator
>> >>
>> >> This would allow me to first define any DataSetDefinition (describing
>> >> any N-by-N table, essentially, with SqlDataSetDefinition being the
>> >> DataSetDefinition type that matches the use case driving this
>> >> discussion). Then, DataSetColumnIndicator would require my
>> >> DataSetDefinition, a column name (or index), and an aggregator (COUNT,
>> >> SUM, AVG, MIN, MAX, MEDIAN, SINGLE_VALUE, etc... ) along with optional
>> >> Filters and Parameters. This would be quite powerful, especially if
>> >> we're caching evaluated DataSetDefinitions at run-time so that you can
>> >> evaluate a bunch of indicators in memory after having run a single
>> >> query to load the DataSet (the N-by-N table). And of course, our
>> >> DataSetColumnIndicator could be made to optionally handle a
>> >> denominator calculation, and dimensions (like CohortIndicator).
>> >>
>> >> Then, for Bob's case, where the SQL is already written and already
>> >> returns fully calculated indicator values, SqlIndicator would be a
>> >> specific case of the above -- sort of a convenience Indicator
>> >> implementation. I envision SqlIndicator to be an extension of
>> >> DataSetColumnIndicator that adds the method setSqlQuery(String
>> >> sqlQuery). setSqlQuery would expect a query string that returns the
>> >> indicator value (i.e. a 1-by-1 result). Behind the scenes,
>> >> SqlIndicator.setQuery(String sqlQuery) would really be adding a new
>> >> SqlDataSetDefinition(sqlQuery), an aggregator of SINGLE_VALUE, and a
>> >> first-position column index value.
>> >>
>> >> Then, both of the above would use the same evaluator, which would
>> >> build the DataSet, and then calculate indicators against the columns.
>> >> The SqlIndicator case would return a 1 by 1 table, and return the
>> >> column value. DataSetColumnIndicator could be used to do fancier
>> >> calculations against bigger tables.
>> >>
>> >> My 2 cents. I haven't looked at the sdmx-hd module, but it should be
>> >> too hard to change it to use a more generic Indicator type that can be
>> >> extended by Indicator types in the reporting module.
>> >>
>> >> d
>> >>
>> >>
>> >>
>> >> On Tue, Aug 23, 2011 at 12:20 PM, Bob Jolliffe<[email protected]>
>> wrote:
>> >>> On 23 August 2011 00:49, Darius Jazayeri<[email protected]>
>> wrote:
>> >>>> Just to remind myself, you already have a bunch of SQL queries
>> >>>> written
>> (at
>> >>>> possibly great pain) which do all the calculations you want, right?
>> >>> yes
>> >>>> Is there
>> >>>> some reason you might want to rewrite those in some mechanism other
>> than
>> >>>> SQL?
>> >>> No. Though there are some tricky ones eg. related to bed occupancy
>> >>> rate which might benefit from some java assistance ... but lets not
>> >>> worry about that yet. I believe Chuyen has got most of what is
>> >>> required in SQL
>> >>>
>> >>>> Do those queries already include the male/female, adult/child, etc,
>> >>>> breakdown?
>> >>> There is surprizingly little disaggregation required. For example, as
>> >>> it stands, we collect total OPD admissions but not disaggregated by
>> >>> gender or sex. My sense is we probably we should as someone will
>> >>> decide its a requirement further down the road. Also similar
>> >>> scenarios elsewhere will certainly have this type of disaggregation
>> >>> (I'm thinking particularly of Kenys where there is a substantial
>> >>> openmrs installed base which will also generate facility reports).
>> >>>
>> >>>> Do you want to somehow reuse the existing cohort-based
>> >>>> dimensions?
>> >>>> And you want to be able to run a report that contains both
>> count-of-patient
>> >>>> indicators (or maybe not even these?) and these SQL-based indicators,
>> then
>> >>>> export that via SDMX-HD.
>> >>> Well there are two reasons we wanted to use the cohort indicators: (i)
>> >>> they were the only indicators available and (ii) the SDMX-HD
>> >>> integration module uses them. It's now clear that what we really need
>> >>> is not the cohort indicators but something with a very similar
>> >>> interface. Like the existing reportobjectgroup
>> >>> or proposed sqldataset indicators. This would require modifying the
>> >>> sdmx-hd module to map to this kind of indicator instead. I think
>> >>> ideally the sdmx-hd module would map against a polymorphic Indicator
>> >>> rather than specific species of Indicator. As I think Mike had
>> >>> indicated in a previous mail, it should "just work". Well it doesn't
>> >>> quite currently because the Indicator interface is just a placeholder,
>> >>> but it could.
>> >>>
>> >>>> Does the UI presentation of running this report
>> >>>> matter? (I assume you don't need the count-of-patient indicators to
>> have
>> >>>> clickable counts that show you the number of patients.) How about the
>> UI of
>> >>>> defining the indicators? Does there need to be one, or can the
>> definitions
>> >>>> be created in-memory, as in the rwanda reports module?
>> >>> Been thinking a bit about this. Short term we could use either of the
>> >>> above two existing approaches, though maintaining a programmer on site
>> >>> isn't going to scale. Neither defining indicators one by one through
>> >>> the UI or defining them in code like the rwanda reports seems to me
>> >>> ideal. Generally the indicators you need to produce you are going to
>> >>> get from elsewhere ie. we don't necessarily report what we want to
>> >>> report. For example exported from dhis2 or (worst case) an excel
>> >>> spreadsheet or (best case) presented as a SDMX Data Structure
>> >>> Definition message (a DSD defines lists of indicators, dataelements,
>> >>> codelists for disaggregation etc). I suspect ideally we want to
>> >>> import the list of indicators and then just configure the underlying
>> >>> sql queries through the UI. So perhaps this would be a variant on the
>> >>> rwanda reports approach - eg. maintain a table of dataelements with
>> >>> sql defintions, almost like a table of stored procedures. Then create
>> >>> reports off this using code similar to that in rwanda reports.
>> >>>
>> >>> As an aside, Viet and I were quite confused last week when we started
>> >>> created cohort queries and indicators and then struggled to find them
>> >>> in the database. Then discovered serializedobject. I suppose
>> >>> populating a list of indicators would mean populating that table with
>> >>> reporting objects. This is probably not the time and place to bring
>> >>> in persistence strategy, but these xstreamed objects feel a bit
>> >>> convoluted to me. But that's a subjective thing ... I can see how
>> >>> recursively saving report elements is attractive.
>> >>>
>> >>> Cheers
>> >>> Bob
>> >>>
>> >>>> -Darius
>> >>>>
>> >>>> On Mon, Aug 22, 2011 at 2:42 PM, Bob Jolliffe<[email protected]>
>> wrote:
>> >>>>> On 22 August 2011 20:57, Darius Jazayeri<[email protected]>
>> wrote:
>> >>>>>> On this week's Design Review Call at 9am Eastern time (our last one
>> of
>> >>>>>> these
>> >>>>>> calls before we switch to the new call schedule, actually) we will
>> be
>> >>>>>> discussing how to solve HISP India's use case with the reporting
>> module
>> >>>>>> in
>> >>>>>> the best/easiest way.
>> >>>>>> (Offhand I imagine that would be implementing a SqlIndicator, but I
>> must
>> >>>>>> admit I haven't followed every aspect of this conversation.)
>> >>>>> Darius thanks for the responsiveness on this.
>> >>>>>
>> >>>>> Offhand it is looking to me that in the short term we might meet our
>> >>>>> requirements through a similar module to the rwandareports and
>> >>>>> objectgroupindicators. Then find ourselves in the same corner as
>> >>>>> Dave, looking at ways to refactor and harmonize on to firmer ground
>> >>>>> :-)
>> >>>>>
>> >>>>> I'm hoping to make the call.
>> >>>>>
>> >>>>> Bob
>> >>>>>
>> >>>>>> -Darius
>> >>>>>>
>> >>>>>> On Mon, Aug 22, 2011 at 9:13 AM, Bob Jolliffe<[email protected]
>> >
>> >>>>>> wrote:
>> >>>>>>> Thanks Dave (and Lara) for the explanations. Some comments below
>> ...
>> >>>>>>>
>> >>>>>>> On 21 August 2011 21:56, Dave Thomas<[email protected]> wrote:
>> >>>>>>>> Sure. Here's an example (see attached; sorry for the formatting
>> --
>> >>>>>>>> i
>> >>>>>>>> recommend setting your texteditor to nowrap).
>> >>>>>>>>
>> >>>>>>>> There's a bit of an explanation necessary here. First, note that
>> the
>> >>>>>>>> SQL returns exactly 2 items -- object_id (of any openmrs object),
>> and
>> >>>>>>>> patient_id. An ObjectGroup is really a Set<Integer[]> where
>> >>>>>>>> the
>> >>>>>>>> Integer[] is expected to contain 2 values -- object_id (any
>> object),
>> >>>>>>>> and THEN the patient_id (the owner of the object). A bit of a
>> hack,
>> >>>>>>>> I
>> >>>>>>>> admit, but it allows for intersection with a base Cohort (as
>> >>>>>>>> discussed
>> >>>>>>>> previously), and the whole operation happens without
>> >>>>>>>> instantiating
>> >>>>>>>> any
>> >>>>>>>> of the objects (Encounter or Patient, in this case), meaning that
>> it
>> >>>>>>>> scales really well. Again, this is all modeled against the core
>> >>>>>>>> notion of a Cohort, and then SqlCohortDefinition,
>> >>>>>>>> CohortIndicator,
>> >>>>>>>> CohortIndicatorResult.
>> >>>>>>>>
>> >>>>>>>> Also, looking at the attached code, i can't remember why the
>> location
>> >>>>>>>> parameter was scoped differently than startDate and endDate,
>> >>>>>>>> which
>> >>>>>>>> needed to be explicitly added to the definition for some reason.
>> >>>>>>>> Maybe this has changed?
>> >>>>>>>>
>> >>>>>>>> Finally, reportingobjectgroup has its own persistence mechanisms
>> for
>> >>>>>>>> sql definitions, indicator definitions, and report definitions.
>> >>>>>>>> I
>> >>>>>>>> had
>> >>>>>>>> to create my own services for these, because at the time, i
>> remember
>> >>>>>>>> not being able to register persistence handlers with the regular
>> >>>>>>>> reporting xstream stuff from another module. I also needed to
>> create
>> >>>>>>>> my own service for the objectgroup evaluator to use. These
>> created a
>> >>>>>>>> ton of redundancy between reporting and reportingobjectgroup.
>> These
>> >>>>>>>> could be merged by having the reporting module allow external
>> >>>>>>>> registration of persistence handlers, and adding some very
>> >>>>>>>> generic
>> >>>>>>>> executeSQL methods to the core reporting service that return
>> values
>> >>>>>>>> according to generic interface types.
>> >>>>>>>>
>> >>>>>>>> Reading through the above, you can see why i'd like to get this
>> >>>>>>>> on
>> >>>>>>>> firmer footing. Based on reading my own explanation, i may try
>> the
>> >>>>>>>> purely SqlDataSetDefinition approach this week, time allowing,
>> just
>> >>>>>>>> to
>> >>>>>>>> see how it goes.
>> >>>>>>>>
>> >>>>>>>> That being said, we have this working just fine in production.
>> Our
>> >>>>>>>> primary care report (from which the attached code excerpt is
>> >>>>>>>> from)
>> >>>>>>>> has about 30 total indicators, and runs in about 10 seconds
>> against
>> >>>>>>>> an
>> >>>>>>>> Obs table with 5.6 million observations.
>> >>>>>>> I have a better idea on what you have done here and I suspect it
>> >>>>>>> contains most of the clues of how to get on a "firmer footing".
>> First
>> >>>>>>> it seems to me the Indicator interface and BaseIndicator class
>> >>>>>>> both
>> >>>>>>> need to be enrichened. There's quite a bit of stuff in
>> >>>>>>> CohortIndicator which can quite easily even now be moved up to
>> >>>>>>> BaseIndicator to avoid some of the replication (as well as
>> constrain)
>> >>>>>>> what you have had to do. Things like aggregateor, logic
>> expression,
>> >>>>>>> indicator type etc.
>> >>>>>>>
>> >>>>>>> Getting into more meat, how about methods like
>> setCohortDefinition(..)
>> >>>>>>> and setObjectGroupDefinition(..)? Do you foresee there could be a
>> >>>>>>> more general setDefinition(..) method in the generic Indicator
>> >>>>>>> interface?
>> >>>>>>>
>> >>>>>>> Regards
>> >>>>>>> Bob
>> >>>>>>>
>> >>>>>>>> d
>> >>>>>>>>
>> >>>>>>>>
>> >>>>>>>>
>> >>>>>>>>
>> >>>>>>>> On Sun, Aug 21, 2011 at 9:08 PM, Bob Jolliffe<
>> [email protected]>
>> >>>>>>>> wrote:
>> >>>>>>>>> Hi Dave
>> >>>>>>>>>
>> >>>>>>>>> Is there some way we can take a look at the support module code
>> to
>> >>>>>>>>> get
>> >>>>>>>>> a better idea how you use it? Sorry, I'm sure its obvious to
>> >>>>>>>>> you
>> >>>>>>>>> but
>> >>>>>>>>> some of us are slow :-)
>> >>>>>>>>>
>> >>>>>>>>> Regards
>> >>>>>>>>> Bob
>> >>>>>>>>>
>> >>>>>>>>> On 20 August 2011 12:00, Dave Thomas<[email protected]> wrote:
>> >>>>>>>>>> Hi. No, the indicator types are probably not exposed through
>> the
>> >>>>>>>>>> UI.
>> >>>>>>>>>> We have another support module that registers our reports using
>> >>>>>>>>>> code...
>> >>>>>>>>>>
>> >>>>>>>>>> d
>> >>>>>>>>>>
>> >>>>>>>>>> On Sat, Aug 20, 2011 at 12:49 PM, Bob Jolliffe
>> >>>>>>>>>> <[email protected]>
>> >>>>>>>>>> wrote:
>> >>>>>>>>>>> On 20 August 2011 09:40, Dave Thomas<[email protected]>
>> wrote:
>> >>>>>>>>>>>> Hi. For reportingobjectgroup, say you write a query that
>> pulls
>> >>>>>>>>>>>> up a
>> >>>>>>>>>>>> group of encounters. If you intersect this with the Cohort
>> 'all
>> >>>>>>>>>>>> patients in a particular program', it will filter out the
>> >>>>>>>>>>>> encounters
>> >>>>>>>>>>>> owned by patients not in the program. Meaning that for any
>> given
>> >>>>>>>>>>>> patient in the program, there can still be N encounters in
>> >>>>>>>>>>>> the
>> >>>>>>>>>>>> result.
>> >>>>>>>>>>>> This, i think, avoids your problem of having duplicate
>> patients
>> >>>>>>>>>>>> in
>> >>>>>>>>>>>> your Cohort query condensing to 1.
>> >>>>>>>>>>> OK. That sounds good. I just installed the module and I
>> >>>>>>>>>>> don't
>> >>>>>>>>>>> quite
>> >>>>>>>>>>> see how to find these new indicator types. Are they not
>> exposed
>> >>>>>>>>>>> through the UI?
>> >>>>>>>>>>>
>> >>>>>>>>>>>> I originally got hung up on the
>> >>>>>>>>>>>> ability to intersect with a Cohort based on the default
>> reporting
>> >>>>>>>>>>>> framework UI, which optionally allows you to select a base
>> >>>>>>>>>>>> Cohort,
>> >>>>>>>>>>>> but
>> >>>>>>>>>>>> in thinking about it, it allows us to ask interesting
>> questions
>> >>>>>>>>>>>> pretty
>> >>>>>>>>>>>> easily. Things like 'to what degree do patients in the HIV
>> >>>>>>>>>>>> program
>> >>>>>>>>>>>> also seek out primary care services outside of their monthly
>> HIV
>> >>>>>>>>>>>> visit' -- this is extremely straightforward using this
>> >>>>>>>>>>>> intersection.
>> >>>>>>>>>>>>
>> >>>>>>>>>>>> Its funny that the perception is that the reporting framework
>> is
>> >>>>>>>>>>>> inflexible, because in truth I've found it to be the
>> >>>>>>>>>>>> opposite.
>> I
>> >>>>>>>>>>>> wrote the reportingobjectgroup module over a week,
>> >>>>>>>>>>> ... I think that defines what I mean by inflexible from the
>> user's
>> >>>>>>>>>>> perspective :-) As a programming framework it seems mighty
>> fine.
>> >>>>>>>>>>> But
>> >>>>>>>>>>> I guess, as Mike has alluded to, the intention was that a
>> >>>>>>>>>>> range
>> of
>> >>>>>>>>>>> different indicator types can be implemented. I guess that is
>> >>>>>>>>>>> where
>> >>>>>>>>>>> we are now.
>> >>>>>>>>>>>
>> >>>>>>>>>>> Cheers
>> >>>>>>>>>>> Bob
>> >>>>>>>>>>>
>> >>>>>>>>>>>> because i was
>> >>>>>>>>>>>> facing the simple problem of counting primary care encounters
>> at
>> >>>>>>>>>>>> a
>> >>>>>>>>>>>> health center over an arbitrary time period (usually a
>> >>>>>>>>>>>> month).
>> >>>>>>>>>>>> So
>> >>>>>>>>>>>> my
>> >>>>>>>>>>>> thought was, 'what if I replicate SqlCohortDefinition
>> >>>>>>>>>>>> functionality,
>> >>>>>>>>>>>> almost exactly, but allow the IDs to correspond to the ID of
>> any
>> >>>>>>>>>>>> OpenmrsObject'. This is the basic definition of an
>> ObjectGroup.
>> >>>>>>>>>>>>
>> >>>>>>>>>>>> I took this approach having cracked open the code of the
>> >>>>>>>>>>>> reporting
>> >>>>>>>>>>>> framework, found CohortSQLDefinition, and then decided to
>> >>>>>>>>>>>> copy
>> >>>>>>>>>>>> it,
>> >>>>>>>>>>>> figuring that it wouldn't be very hard... It of course
>> >>>>>>>>>>>> turned
>> >>>>>>>>>>>> into
>> >>>>>>>>>>>> a
>> >>>>>>>>>>>> bit more of a project than i had expected, but we're using
>> >>>>>>>>>>>> the
>> >>>>>>>>>>>> results
>> >>>>>>>>>>>> in production now, and we've been able to build some nice
>> stuff
>> >>>>>>>>>>>> on
>> >>>>>>>>>>>> it.
>> >>>>>>>>>>>>
>> >>>>>>>>>>>> That being said, I don't really want to maintain the module
>> >>>>>>>>>>>> forever,
>> >>>>>>>>>>>> and Mike has pointed out to me that there were other
>> approaches
>> >>>>>>>>>>>> --
>> >>>>>>>>>>>> one
>> >>>>>>>>>>>> was building a SQLDataSetDefinition with a row-per-encounter
>> >>>>>>>>>>>> structure
>> >>>>>>>>>>>> and then building indicators off of this (not sure how much
>> >>>>>>>>>>>> coding
>> >>>>>>>>>>>> this would take?). Another was writing a LogicDataSource and
>> >>>>>>>>>>>> creating
>> >>>>>>>>>>>> a Rule for 'given a patient, how many times did they come
>> >>>>>>>>>>>> into
>> >>>>>>>>>>>> the
>> >>>>>>>>>>>> health center for a primary care visit over a give
>> timeperiod?'
>> >>>>>>>>>>>> (is
>> >>>>>>>>>>>> there a problem passing Reporting run-time parameters to
>> >>>>>>>>>>>> logic
>> >>>>>>>>>>>> Rules?).
>> >>>>>>>>>>>>
>> >>>>>>>>>>>> I don't have the bandwidth to try all three approaches,
>> >>>>>>>>>>>> unfortunately,
>> >>>>>>>>>>>> and I sort of wish that i had tried the SQLDataSetDefinition
>> >>>>>>>>>>>> first
>> >>>>>>>>>>>> (i'm not sure if it was already implemented at the time?).
>> >>>>>>>>>>>> Ultimately, the goal should be to be able to write 5 lines of
>> >>>>>>>>>>>> code
>> >>>>>>>>>>>> defining the actual SQL you want, for any Object, and then
>> define
>> >>>>>>>>>>>> any
>> >>>>>>>>>>>> number of indicators. I'm not sure which approach is going
>> >>>>>>>>>>>> to
>> >>>>>>>>>>>> ultimately win, but i'm glad we're having this conversation,
>> and
>> >>>>>>>>>>>> i
>> >>>>>>>>>>>> am
>> >>>>>>>>>>>> willing to put in some programming time once there are firm
>> >>>>>>>>>>>> specifications for how exactly a SQLIndicator works.
>> >>>>>>>>>>>> Transferring
>> >>>>>>>>>>>> the
>> >>>>>>>>>>>> objectgroup indicators we have in production to a more
>> >>>>>>>>>>>> core-supported
>> >>>>>>>>>>>> strategy would make me feel like we're on slightly firmer
>> ground.
>> >>>>>>>>>>>>
>> >>>>>>>>>>>> d
>> >>>>>>>>>>>>
>> >>>>>>>>>>>> On Sat, Aug 20, 2011 at 4:04 AM, Michael Seaton<
>> [email protected]>
>> >>>>>>>>>>>> wrote:
>> >>>>>>>>>>>>> I see no problem at all with a SqlIndicator, and I doubt
>> >>>>>>>>>>>>> that
>> >>>>>>>>>>>>> there
>> >>>>>>>>>>>>> is any
>> >>>>>>>>>>>>> reason why this wouldn't "just work" with the SDMX-HD
>> >>>>>>>>>>>>> module.
>> >>>>>>>>>>>>> We
>> >>>>>>>>>>>>> always
>> >>>>>>>>>>>>> envisioned there would be all sorts of Indicator
>> implementations
>> >>>>>>>>>>>>> (or
>> >>>>>>>>>>>>> Aggregated Data Element implementations for you Bob) - the
>> >>>>>>>>>>>>> CohortIndicator
>> >>>>>>>>>>>>> was just meant to be the beginning.
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>> As an alternative (or in addition), we could consider
>> >>>>>>>>>>>>> implementing
>> >>>>>>>>>>>>> something
>> >>>>>>>>>>>>> like a AggregatedDataSetColumnIndicator, which takes in:
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>> 1. A Mapped<DataSetDefinition>
>> >>>>>>>>>>>>> 2. A column name
>> >>>>>>>>>>>>> 3. An Aggregation
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>> Since we already have SqlDataSetDefinitions implemented,
>> which
>> >>>>>>>>>>>>> simply return
>> >>>>>>>>>>>>> a table of Data that can contain absolutely anything, this
>> >>>>>>>>>>>>> indicator would
>> >>>>>>>>>>>>> wrap on of these, and then perform a particular aggregation
>> on
>> >>>>>>>>>>>>> one
>> >>>>>>>>>>>>> of the
>> >>>>>>>>>>>>> columns in order to produce an Indicator value. This would
>> be
>> >>>>>>>>>>>>> pretty easy
>> >>>>>>>>>>>>> to implement...
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>> Bob - are either of these something you would be interested
>> in
>> >>>>>>>>>>>>> taking on
>> >>>>>>>>>>>>> development-wise, or would you need one of us to take this
>> >>>>>>>>>>>>> on
>> >>>>>>>>>>>>> soon?
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>> Mike
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>> On 08/19/2011 05:08 PM, Bob Jolliffe wrote:
>> >>>>>>>>>>>>>> On 19 August 2011 19:18, Dave Thomas<[email protected]>
>> wrote:
>> >>>>>>>>>>>>>>> This is exactly what the reportingobjectgroup module that
>> >>>>>>>>>>>>>>> i
>> >>>>>>>>>>>>>>> wrote
>> >>>>>>>>>>>>>>> was
>> >>>>>>>>>>>>>>> for -- the idea that you might want to use SQL to select
>> >>>>>>>>>>>>>>> groups
>> >>>>>>>>>>>>>>> of any
>> >>>>>>>>>>>>>>> OpenmrsObject, and still be able to intersect it with a
>> base
>> >>>>>>>>>>>>>>> cohort.
>> >>>>>>>>>>>>>> Hi Dave.
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>> I haven't looked at the reportingobjectgroup module yet.
>> That
>> >>>>>>>>>>>>>> was
>> >>>>>>>>>>>>>> going to be step two after we figured out the "easy"
>> reported
>> >>>>>>>>>>>>>> dataelements which involved counting heads rather than
>> counting
>> >>>>>>>>>>>>>> other
>> >>>>>>>>>>>>>> openmrs objects. And I'm not sure that I understand fully
>> the
>> >>>>>>>>>>>>>> nuances
>> >>>>>>>>>>>>>> of what you say above but I am worried that you still want
>> to
>> >>>>>>>>>>>>>> intersect with a base cohort ... as long as we are talking
>> of a
>> >>>>>>>>>>>>>> cohort
>> >>>>>>>>>>>>>> then I'm guessing we don't count the same person twice. So
>> if
>> >>>>>>>>>>>>>> I
>> >>>>>>>>>>>>>> want
>> >>>>>>>>>>>>>> to know how many opd encounters there were last month,
>> >>>>>>>>>>>>>> "intersecting"
>> >>>>>>>>>>>>>> this with a base cohort sounds like it will filter out the
>> >>>>>>>>>>>>>> duplicates
>> >>>>>>>>>>>>>> which will again produce an incorrect result. Or maybe I
>> >>>>>>>>>>>>>> am
>> >>>>>>>>>>>>>> wrong
>> >>>>>>>>>>>>>> -
>> >>>>>>>>>>>>>> sorry to be speaking from ignorance having not yet looked
>> >>>>>>>>>>>>>> at
>> >>>>>>>>>>>>>> the
>> >>>>>>>>>>>>>> module.
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>> I'd really like to talk strategy about how to roll this
>> module
>> >>>>>>>>>>>>>>> into
>> >>>>>>>>>>>>>>> reporting core (or substitute a core solution for the
>> things
>> >>>>>>>>>>>>>>> we
>> >>>>>>>>>>>>>>> already have built on it).
>> >>>>>>>>>>>>>> From our perspective (at least me and Viet :-) ) we are
>> >>>>>>>>>>>>>> looking
>> >>>>>>>>>>>>>> for a
>> >>>>>>>>>>>>>> sweet spot. The implementors of the highly customized
>> version
>> >>>>>>>>>>>>>> of
>> >>>>>>>>>>>>>> openmrs running in Shimla, India, have already to a large
>> >>>>>>>>>>>>>> extent
>> >>>>>>>>>>>>>> "solved" their reporting problems by creating Birt reports
>> >>>>>>>>>>>>>> containing
>> >>>>>>>>>>>>>> the various odds and sods of aggregate dataelements they
>> need
>> >>>>>>>>>>>>>> to
>> >>>>>>>>>>>>>> produce. The flexibility of using birt meant they could
>> >>>>>>>>>>>>>> execute
>> >>>>>>>>>>>>>> whatever queries they want to populate the various reports.
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>> The downside being that the query, the data and the
>> >>>>>>>>>>>>>> presentation
>> >>>>>>>>>>>>>> all
>> >>>>>>>>>>>>>> become hopelessly entangled in the birt report. And this
>> >>>>>>>>>>>>>> doesn't
>> >>>>>>>>>>>>>> really help when you want to produce data to be consumed by
>> >>>>>>>>>>>>>> another
>> >>>>>>>>>>>>>> system (eg dhis). Its possible of course to extract the
>> data
>> >>>>>>>>>>>>>> from
>> >>>>>>>>>>>>>> the
>> >>>>>>>>>>>>>> birt report but that is a bit of a hack, particularly when
>> you
>> >>>>>>>>>>>>>> need to
>> >>>>>>>>>>>>>> map the anonymous birt dataelements. Having aggregated
>> >>>>>>>>>>>>>> dataelement
>> >>>>>>>>>>>>>> (or indicator) objects defined within the system makes much
>> >>>>>>>>>>>>>> more
>> >>>>>>>>>>>>>> sense.
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>> The strength of the reporting module, and why I have been
>> its
>> >>>>>>>>>>>>>> loudest
>> >>>>>>>>>>>>>> advocate, is that it separates the notion of reported
>> >>>>>>>>>>>>>> dataelements
>> >>>>>>>>>>>>>> (or
>> >>>>>>>>>>>>>> Indicators as they are known as in this context) from the
>> >>>>>>>>>>>>>> rendering or
>> >>>>>>>>>>>>>> presentation of reports. I am going to continue to use the
>> >>>>>>>>>>>>>> term
>> >>>>>>>>>>>>>> aggregate dataelement rather than indicator, but otherwise
>> the
>> >>>>>>>>>>>>>> semantics are not that important for the current
>> >>>>>>>>>>>>>> discussion.
>> >>>>>>>>>>>>>> The
>> >>>>>>>>>>>>>> ability to define aggregate dataelements, datasets and
>> >>>>>>>>>>>>>> composite
>> >>>>>>>>>>>>>> reports independently of how they are rendered is really a
>> >>>>>>>>>>>>>> powerful
>> >>>>>>>>>>>>>> and even essential notion if we are to have a reporting
>> >>>>>>>>>>>>>> capability
>> >>>>>>>>>>>>>> which meets a wide variety of use cases. So the reporting
>> >>>>>>>>>>>>>> module
>> >>>>>>>>>>>>>> really does move in the right direction ...
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>> But at the highest level of abstraction, an aggregate
>> >>>>>>>>>>>>>> dataelement
>> >>>>>>>>>>>>>> object need only have a name, a description, and a
>> >>>>>>>>>>>>>> mechanism
>> >>>>>>>>>>>>>> (query)
>> >>>>>>>>>>>>>> for deriving a value. It should not be a cast iron
>> requirement
>> >>>>>>>>>>>>>> that
>> >>>>>>>>>>>>>> there is an underlying cohort derived directly, or via an
>> >>>>>>>>>>>>>> intersection. I can see for many cases this is very useful
>> ...
>> >>>>>>>>>>>>>> ie
>> >>>>>>>>>>>>>> to
>> >>>>>>>>>>>>>> have an underlying cohort to drill down into. But equally
>> >>>>>>>>>>>>>> often
>> >>>>>>>>>>>>>> it is
>> >>>>>>>>>>>>>> not and all you want is a count or some other aggregation.
>> So
>> >>>>>>>>>>>>>> what we
>> >>>>>>>>>>>>>> find currently is that people argue that using the
>> >>>>>>>>>>>>>> reporting
>> >>>>>>>>>>>>>> module is
>> >>>>>>>>>>>>>> too inflexible and don't understand why we can produce
>> certain
>> >>>>>>>>>>>>>> reports
>> >>>>>>>>>>>>>> relatively simply in birt, but not in the reporting module.
>> >>>>>>>>>>>>>> And
>> >>>>>>>>>>>>>> naturally conclude that we should stick with Birt.
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>> In order to find the sweet spot of retaining the
>> >>>>>>>>>>>>>> flexibility
>> of
>> >>>>>>>>>>>>>> birt
>> >>>>>>>>>>>>>> together with the organising structure of the reporting
>> module,
>> >>>>>>>>>>>>>> it
>> >>>>>>>>>>>>>> seems we need to have a class of aggregate dataelement
>> >>>>>>>>>>>>>> whose
>> >>>>>>>>>>>>>> only
>> >>>>>>>>>>>>>> constraint is that it must result in a number, but which
>> >>>>>>>>>>>>>> can
>> be
>> >>>>>>>>>>>>>> derived from any sql query. I think this is what Darius is
>> >>>>>>>>>>>>>> also
>> >>>>>>>>>>>>>> foreseeing in his SqlIndicator. If we had such
>> SqlIndicators,
>> >>>>>>>>>>>>>> we
>> >>>>>>>>>>>>>> could (i) reuse the queries which have already been
>> developed
>> >>>>>>>>>>>>>> for
>> >>>>>>>>>>>>>> birt
>> >>>>>>>>>>>>>> and (ii) render the resulting reports in various ways. I
>> think
>> >>>>>>>>>>>>>> I
>> >>>>>>>>>>>>>> even
>> >>>>>>>>>>>>>> suggested in a previous mail that an ideal outcome of this
>> >>>>>>>>>>>>>> might
>> >>>>>>>>>>>>>> be
>> >>>>>>>>>>>>>> that one of the possible renderings could in fact be a Birt
>> >>>>>>>>>>>>>> report, in
>> >>>>>>>>>>>>>> which case we will have closed the circle and have
>> >>>>>>>>>>>>>> available
>> >>>>>>>>>>>>>> all
>> >>>>>>>>>>>>>> the
>> >>>>>>>>>>>>>> presentation capabilities of Birt, but separated the
>> definition
>> >>>>>>>>>>>>>> of
>> >>>>>>>>>>>>>> aggregated dataelements from the presentation of them.
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>> Apologies if I have misinterpreted many things re cohorts,
>> >>>>>>>>>>>>>> intersections etc. And if the reportingobject module meets
>> the
>> >>>>>>>>>>>>>> above
>> >>>>>>>>>>>>>> requirement then I am already delighted. I'm going to look
>> at
>> >>>>>>>>>>>>>> it
>> >>>>>>>>>>>>>> tonight ...
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>> Regards
>> >>>>>>>>>>>>>> Bob
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>> d
>> >>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>> On Fri, Aug 19, 2011 at 5:30 PM, Darius Jazayeri
>> >>>>>>>>>>>>>>> <[email protected]> wrote:
>> >>>>>>>>>>>>>>>> We talked off-list, and it turns out that:
>> >>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>> Many/most of the indicators Bob wants to build are not
>> really
>> >>>>>>>>>>>>>>>> cohort
>> >>>>>>>>>>>>>>>> indicators, but rather counts of encounters, obs, log
>> >>>>>>>>>>>>>>>> entries,
>> >>>>>>>>>>>>>>>> etc.
>> >>>>>>>>>>>>>>>> They'd mostly be calculated via SQL.
>> >>>>>>>>>>>>>>>> They need to be able to export these via the sdmx-hd
>> module,
>> >>>>>>>>>>>>>>>> into DHIS.
>> >>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>> @Mike, @Ryan,
>> >>>>>>>>>>>>>>>> If we were to do a SqlIndicator implementation (which
>> >>>>>>>>>>>>>>>> wouldn't
>> >>>>>>>>>>>>>>>> be too
>> >>>>>>>>>>>>>>>> much
>> >>>>>>>>>>>>>>>> work), would that easily fit into the current SDMX-HD
>> export
>> >>>>>>>>>>>>>>>> module? Or
>> >>>>>>>>>>>>>>>> is
>> >>>>>>>>>>>>>>>> that hardcoded to cohort indicators? And how much work
>> would
>> >>>>>>>>>>>>>>>> it
>> >>>>>>>>>>>>>>>> be to
>> >>>>>>>>>>>>>>>> change
>> >>>>>>>>>>>>>>>> that?
>> >>>>>>>>>>>>>>>> -Darius
>> >>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>> On Fri, Aug 19, 2011 at 7:33 AM, Bob
>> >>>>>>>>>>>>>>>> Jolliffe<[email protected]>
>> >>>>>>>>>>>>>>>> wrote:
>> >>>>>>>>>>>>>>>>> On 19 August 2011 15:07, Darius
>> >>>>>>>>>>>>>>>>> Jazayeri<[email protected]>
>> >>>>>>>>>>>>>>>>> wrote:
>> >>>>>>>>>>>>>>>>>> You're not doing a count distinct, so if your
>> >>>>>>>>>>>>>>>>>> opd_patient_queue_log
>> >>>>>>>>>>>>>>>>>> can
>> >>>>>>>>>>>>>>>>>> have
>> >>>>>>>>>>>>>>>>>> the same patient_id more than once, that'd be why you
>> get a
>> >>>>>>>>>>>>>>>>>> difference.
>> >>>>>>>>>>>>>>>>>> -Darius
>> >>>>>>>>>>>>>>>>> Thanks Darius. You are absolutely right. I also just
>> >>>>>>>>>>>>>>>>> figured
>> >>>>>>>>>>>>>>>>> that
>> >>>>>>>>>>>>>>>>> out a few minutes ago.
>> >>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>> Though it has left me with a sinking feeling about how
>> >>>>>>>>>>>>>>>>> to
>> >>>>>>>>>>>>>>>>> use
>> >>>>>>>>>>>>>>>>> the
>> >>>>>>>>>>>>>>>>> reporting module. It makes sense now that the penny has
>> >>>>>>>>>>>>>>>>> slowly
>> >>>>>>>>>>>>>>>>> dropped, that a cohort query is in fact a query to
>> >>>>>>>>>>>>>>>>> select
>> a
>> >>>>>>>>>>>>>>>>> distinct
>> >>>>>>>>>>>>>>>>> group, or cohort, of patients. Which you could then
>> drill
>> >>>>>>>>>>>>>>>>> down
>> >>>>>>>>>>>>>>>>> into
>> >>>>>>>>>>>>>>>>> etc.
>> >>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>> But at the level of a typical service indicator, I am
>> really
>> >>>>>>>>>>>>>>>>> not
>> >>>>>>>>>>>>>>>>> interested in who the individual patients are. I need
>> >>>>>>>>>>>>>>>>> to
>> >>>>>>>>>>>>>>>>> know
>> >>>>>>>>>>>>>>>>> how
>> >>>>>>>>>>>>>>>>> many patients had OPD encounters this month, for
>> >>>>>>>>>>>>>>>>> example.
>> >>>>>>>>>>>>>>>>> Using a
>> >>>>>>>>>>>>>>>>> cohort query for this seemed to make sense, but of
>> >>>>>>>>>>>>>>>>> course
>> it
>> >>>>>>>>>>>>>>>>> doesn't
>> >>>>>>>>>>>>>>>>> as it filters the duplicate patients. So I should in
>> fact
>> >>>>>>>>>>>>>>>>> be
>> >>>>>>>>>>>>>>>>> counting
>> >>>>>>>>>>>>>>>>> the encounters rather than the patients, but then its
>> >>>>>>>>>>>>>>>>> not
>> a
>> >>>>>>>>>>>>>>>>> cohort
>> >>>>>>>>>>>>>>>>> query :-(
>> >>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>> On Fri, Aug 19, 2011 at 5:37 AM, Bob
>> >>>>>>>>>>>>>>>>>> Jolliffe<[email protected]>
>> >>>>>>>>>>>>>>>>>> wrote:
>> >>>>>>>>>>>>>>>>>>> I am trying to compose an indicator which makes use of
>> a
>> >>>>>>>>>>>>>>>>>>> join
>> >>>>>>>>>>>>>>>>>>> with a
>> >>>>>>>>>>>>>>>>>>> custom table.
>> >>>>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>>> Does anyone have an idea why executing the query
>> directly
>> >>>>>>>>>>>>>>>>>>> as:
>> >>>>>>>>>>>>>>>>>>> mysql -u ... -e 'Select count(patient.patient_id) from
>> >>>>>>>>>>>>>>>>>>> patient inner
>> >>>>>>>>>>>>>>>>>>> join opd_patient_queue_log on
>> >>>>>>>>>>>>>>>>>>> patient.patient_id=opd_patient_queue_log.patient_id'
>> >>>>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>>> results in 16593,
>> >>>>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>>> but when I create a sql cohort query as above (without
>> the
>> >>>>>>>>>>>>>>>>>>> count), I
>> >>>>>>>>>>>>>>>>>>> get a result of 13592.
>> >>>>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>>> How does openmrs count the size of the resultset? It
>> >>>>>>>>>>>>>>>>>>> seems
>> >>>>>>>>>>>>>>>>>>> its not a
>> >>>>>>>>>>>>>>>>>>> simple count ...
>> >>>>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>>> Regards
>> >>>>>>>>>>>>>>>>>>> Bob
>> >>>>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>>> _________________________________________
>> >>>>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>>> To unsubscribe from OpenMRS Developers' mailing list,
>> send
>> >>>>>>>>>>>>>>>>>>> an
>> >>>>>>>>>>>>>>>>>>> e-mail
>> >>>>>>>>>>>>>>>>>>> to
>> >>>>>>>>>>>>>>>>>>> [email protected] with "SIGNOFF
>> openmrs-devel-l"
>> >>>>>>>>>>>>>>>>>>> in
>> >>>>>>>>>>>>>>>>>>> the
>> >>>>>>>>>>>>>>>>>>> body
>> >>>>>>>>>>>>>>>>>>> (not
>> >>>>>>>>>>>>>>>>>>> the subject) of your e-mail.
>> >>>>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>>> [mailto:[email protected]
>> ?body=SIGNOFF%20openmrs-devel-l]
>> >>>>>>>>>>>>>>>>>> ________________________________
>> >>>>>>>>>>>>>>>>>> Click here to unsubscribe from OpenMRS Developers'
>> mailing
>> >>>>>>>>>>>>>>>>>> list
>> >>>>>>>>>>>>>>>>> _________________________________________
>> >>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>> To unsubscribe from OpenMRS Developers' mailing list,
>> send
>> >>>>>>>>>>>>>>>>> an
>> >>>>>>>>>>>>>>>>> e-mail to
>> >>>>>>>>>>>>>>>>> [email protected] with "SIGNOFF
>> openmrs-devel-l"
>> >>>>>>>>>>>>>>>>> in
>> >>>>>>>>>>>>>>>>> the body
>> >>>>>>>>>>>>>>>>> (not
>> >>>>>>>>>>>>>>>>> the subject) of your e-mail.
>> >>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>>> [mailto:[email protected]
>> ?body=SIGNOFF%20openmrs-devel-l]
>> >>>>>>>>>>>>>>>> ________________________________
>> >>>>>>>>>>>>>>>> Click here to unsubscribe from OpenMRS Developers'
>> >>>>>>>>>>>>>>>> mailing
>> >>>>>>>>>>>>>>>> list
>> >>>>>>>>>>>>>>> _________________________________________
>> >>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>> To unsubscribe from OpenMRS Developers' mailing list, send
>> an
>> >>>>>>>>>>>>>>> e-mail to
>> >>>>>>>>>>>>>>> [email protected] with "SIGNOFF openmrs-devel-l"
>> in
>> >>>>>>>>>>>>>>> the
>> >>>>>>>>>>>>>>> body (not
>> >>>>>>>>>>>>>>> the subject) of your e-mail.
>> >>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>> [mailto:[email protected]
>> ?body=SIGNOFF%20openmrs-devel-l]
>> >>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>> _________________________________________
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>> To unsubscribe from OpenMRS Developers' mailing list, send
>> an
>> >>>>>>>>>>>>>> e-mail to
>> >>>>>>>>>>>>>> [email protected] with "SIGNOFF openmrs-devel-l"
>> in
>> >>>>>>>>>>>>>> the
>> >>>>>>>>>>>>>> body (not
>> >>>>>>>>>>>>>> the subject) of your e-mail.
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>> [mailto:[email protected]
>> ?body=SIGNOFF%20openmrs-devel-l]
>> >>>>>>>>>>>>> _________________________________________
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>> To unsubscribe from OpenMRS Developers' mailing list, send
>> >>>>>>>>>>>>> an
>> >>>>>>>>>>>>> e-mail to
>> >>>>>>>>>>>>> [email protected] with "SIGNOFF openmrs-devel-l"
>> in
>> >>>>>>>>>>>>> the
>> >>>>>>>>>>>>> body (not
>> >>>>>>>>>>>>> the subject) of your e-mail.
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>> [mailto:[email protected]
>> ?body=SIGNOFF%20openmrs-devel-l]
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>> _________________________________________
>> >>>>>>>>>>>>
>> >>>>>>>>>>>> To unsubscribe from OpenMRS Developers' mailing list, send an
>> >>>>>>>>>>>> e-mail
>> >>>>>>>>>>>> to [email protected] with "SIGNOFF openmrs-devel-l"
>> in
>> >>>>>>>>>>>> the body
>> >>>>>>>>>>>> (not the subject) of your e-mail.
>> >>>>>>>>>>>>
>> >>>>>>>>>>>>
>> >>>>>>>>>>>> [mailto:[email protected]
>> ?body=SIGNOFF%20openmrs-devel-l]
>> >>>>>>>>>>>>
>> >>>>>>>>>>> _________________________________________
>> >>>>>>>>>>>
>> >>>>>>>>>>> To unsubscribe from OpenMRS Developers' mailing list, send an
>> >>>>>>>>>>> e-mail
>> >>>>>>>>>>> to [email protected] with "SIGNOFF openmrs-devel-l"
>> in
>> >>>>>>>>>>> the body
>> >>>>>>>>>>> (not the subject) of your e-mail.
>> >>>>>>>>>>>
>> >>>>>>>>>>>
>> >>>>>>>>>>> [mailto:[email protected]
>> ?body=SIGNOFF%20openmrs-devel-l]
>> >>>>>>>>>>>
>> >>>>>>>>>> _________________________________________
>> >>>>>>>>>>
>> >>>>>>>>>> To unsubscribe from OpenMRS Developers' mailing list, send an
>> >>>>>>>>>> e-mail
>> >>>>>>>>>> to [email protected] with "SIGNOFF openmrs-devel-l"
>> in
>> >>>>>>>>>> the body
>> >>>>>>>>>> (not the subject) of your e-mail.
>> >>>>>>>>>>
>> >>>>>>>>>> [mailto:[email protected]
>> ?body=SIGNOFF%20openmrs-devel-l]
>> >>>>>>>>>>
>> >>>>>>>>> _________________________________________
>> >>>>>>>>>
>> >>>>>>>>> To unsubscribe from OpenMRS Developers' mailing list, send an
>> e-mail
>> >>>>>>>>> to
>> >>>>>>>>> [email protected] with "SIGNOFF openmrs-devel-l" in
>> the
>> >>>>>>>>> body (not
>> >>>>>>>>> the subject) of your e-mail.
>> >>>>>>>>>
>> >>>>>>>>> [mailto:[email protected]
>> ?body=SIGNOFF%20openmrs-devel-l]
>> >>>>>>>>>
>> >>>>>>>> _________________________________________
>> >>>>>>>>
>> >>>>>>>> To unsubscribe from OpenMRS Developers' mailing list, send an
>> e-mail
>> >>>>>>>> to
>> >>>>>>>> [email protected] with "SIGNOFF openmrs-devel-l" in the
>> >>>>>>>> body (not
>> >>>>>>>> the subject) of your e-mail.
>> >>>>>>>>
>> >>>>>>>> [mailto:[email protected]
>> ?body=SIGNOFF%20openmrs-devel-l]
>> >>>>>>>>
>> >>>>>>> _________________________________________
>> >>>>>>>
>> >>>>>>> To unsubscribe from OpenMRS Developers' mailing list, send an
>> e-mail to
>> >>>>>>> [email protected] with "SIGNOFF openmrs-devel-l" in the
>> body
>> >>>>>>> (not
>> >>>>>>> the subject) of your e-mail.
>> >>>>>>>
>> >>>>>>> [mailto:[email protected]
>> ?body=SIGNOFF%20openmrs-devel-l]
>> >>>>>> ________________________________
>> >>>>>> Click here to unsubscribe from OpenMRS Developers' mailing list
>> >>>>> _________________________________________
>> >>>>>
>> >>>>> To unsubscribe from OpenMRS Developers' mailing list, send an e-mail
>> to
>> >>>>> [email protected] with "SIGNOFF openmrs-devel-l" in the
>> body (not
>> >>>>> the subject) of your e-mail.
>> >>>>>
>> >>>>> [mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l]
>> >>>> ________________________________
>> >>>> Click here to unsubscribe from OpenMRS Developers' mailing list
>> >>> _________________________________________
>> >>>
>> >>> To unsubscribe from OpenMRS Developers' mailing list, send an e-mail
>> >>> to
>> [email protected] with "SIGNOFF openmrs-devel-l" in the body
>> (not the subject) of your e-mail.
>> >>>
>> >>> [mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l]
>> >>>
>> >> _________________________________________
>> >>
>> >> To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to
>> [email protected] with "SIGNOFF openmrs-devel-l" in the body
>> (not the subject) of your e-mail.
>> >>
>> >> [mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l]
>> >
>> >_________________________________________
>> >
>> >To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to
>> [email protected] with "SIGNOFF openmrs-devel-l" in the body
>> (not the subject) of your e-mail.
>> >
>> >[mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l]
>>
>
> _________________________________________
>
> To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to
> [email protected] with "SIGNOFF openmrs-devel-l" in the body (not
> the subject) of your e-mail.
>
> [mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l]
>
_________________________________________
To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to
[email protected] with "SIGNOFF openmrs-devel-l" in the body (not
the subject) of your e-mail.
[mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l]