Hi all,
Thanks to everyone who participated in this call today - I think we made
a lot of progress. Here is a summary of what we were able to produce
(full etherpad notes here
<http://notes.openmrs.org/Design-Forum-2011-11-16>):
*
Requirements/Goals*
* Existing logic engine can be implemented as an optional module
without too much trouble.
* A shared Rule interface
o Rules provide a method to evaluate within a context + parameters
* A shared Result interface
o Result declares it's type explicitly
o Provides a mechanism for consumers to easily distinguish
between & use lists vs. single values
* Provides an easy way to coerce results between different types and
between lists vs. single value
* A shared LogicContext interface within which rules are evaluated
* Centralized token registration by providing token, rule ID,
provider, and configuration (unique across providers)
* Support for parameters.
* Caching is deferred to rule evaluators for now
*Skeleton Interfaces / Implementations*
_*Rule:*_
interface *Rule* {
public Set<RuleParameterInfo> getParameterList(); // TODO: We didn't
discuss this interface, but we did agree that rules need to support
parameters
}
interface *RuleEvaluator* {
boolean canEvaluate(Rule rule); // TODO: This might be better
implemented through annotations. Needs further discussion
Map<Integer, Result> evaluate(Cohort, Rule, Map<String, Object>,
RuleContext); // TODO: We probably want to wrap Map<Integer, Result> in
a proper class
}
interface *RuleProvider* {
Rule getRuleInstance(String ruleName, String extraConfig); //
ruleName could be anything the provider wants. might typically be a
classname.
}
interface / implementation *RuleService*/*RuleServiceImpl* {
RuleContext createContext(); // Ensures that RuleContext can be
overridden as needed
void registerToken(String token, RuleProvider provider, String
ruleName, String extraConfig);
void unregisterToken(String token, RuleProvider provider); //
provider for safety
(the below methods might also have implementations without
RuleContext and/or without params for convenience)
Result evaluate(Integer ptId, String token, Map<String, Object>
params, RuleContext context) {
// create a cohort with a single patient, call the evaluate method
on the cohort, return the result for that patient
}
Map<Integer, Result> evaluate(Cohort c, String token, Map<String,
Object> params, RuleContext context) {
// find the appropriate RuleProvider, ruleName, extraConfig for the
given token; get the Rule from the RuleProvider passing in the ruleName
and extraConfig; call evaluate method on the Rule
}
Result evaluate(Integer ptId, Rule rule, Map<String, Object> params,
RuleContext context) {
// construct a cohort of one; get the evaluator for the passed
rule; call evaluate passing in the cohort, params and the context;
return the result for the patient
}
Map<Integer, Result> evaluate(Cohort c, Rule rule, Map<String,
Object> params, RuleContext context) {
// get the evaluator for the passed rule; call evaluate passing in
the cohort, params and the context; return the result
}
}
interface *RuleContext* {
// TODO: Figure out whether this has indexDate, any caching, etc.
}
_*Result:*_
interface Result {
public Object getValue();
public Date getDatetime();// Needs more discussion if this is
appropriate on the base interface (eg. what to do for Lists)
public String formatAsString();
}
class NumericResult {
private Double result;
private Date datetime;
public Object getValue() { return result; }
public Date getDatetime() { return datetime; }
}
class ListResult {
private List<Result> results;
public Object getValue() { return results; }
public Date getDatetime() { // Not sure what to do here. Get the
datetime of the first result? Maybe this method doesn't belong
}
class ObsResult {
private Obs obs;
public getValue() { return obs; }
public Date getDatetime() { return obs.getObsDatetime(); }
}
_*Use of Result:*_
For coercing / converting Results to scalars for use in comparisons etc,
for example:
if (eval("BMI") > 23) { ... }, we discussed a few possible approaches:
(TODO: Decide on one)
1. adding methods like "asDouble()", "asDate()", "asString()",
"asBoolean()" to the Result interface (as we have now)
2. Add single method to Result like: eval("BMI").coerce(Double.class) > 23
3. Add utility method like: LogicUtil.toDouble(eval("BMI")) > 23
4. Add utility method like: LogicUtil.coerce(eval("BMI"), Double.class)
> 23
5. Add a variety of converters like: new
DoubleConverter().convert(eval("BMI")) > 23
Benefit of #5 is that it is cleaner than a massive utility method with
lots of conditional logic, and that it allows modules to plug new
converters into the framework as needed. It might look something like this:
interface ResultConverter<T> {
public T convert(Result);
}
class DoubleConverter<Double> {
public Double convert(Result result) { return
Double.valueOf(result.toString()); }
}
We ran out of time before we could really discuss next steps on all of
this. Should we carve out time in another design forum in December?
Thanks,
Mike
On 11/16/2011 03:23 AM, Ben Wolfe wrote:
Lets plan on having a discussion today about this on the design call
at 2pm. We can write up the solutions (and new questions) for Tammy
to read offline.
Ben
On Tue, Nov 15, 2011 at 2:21 PM, Dugan, Tammy Marie <[email protected]
<mailto:[email protected]>> wrote:
I am on maternity leave and have my hands pretty full with a two
week old and a two year old. If you have any specific questions,
please feel free to email me. Email is a lot easier for me right
now than phone.
Thanks,
Tammy Dugan
Quoting Michael Seaton <[email protected] <mailto:[email protected]>>:
Yes, I will be there and looking forward to talking about this.
Mike
From: [email protected] <mailto:[email protected]>
[mailto:[email protected] <mailto:[email protected]>] On Behalf Of
Ben Wolfe
Sent: Tuesday, November 15, 2011 3:14 AM
To: [email protected]
<mailto:[email protected]>
Subject: Re: [OPENMRS-DEV] The future of Logic
Would it be possible for folks to join tomorrow the 16th at
2pm EST?
I fear that some people will be missing next Wednesday due to
the US
holiday on Thursday the 24th.
Tammy? Dave? Mike? Darius? Burke? Roger? Win? Beuller?
https://wiki.openmrs.org/display/RES/Design+Forum
Ben
On Mon, Nov 14, 2011 at 8:55 PM, Burke Mamlin
<[email protected]
<mailto:[email protected]><mailto:[email protected]
<mailto:[email protected]>>>
wrote:
Can we enumerate what we want from the simplified logic service?
* Existing logic engine can be implemented as an optional module
without too much trouble.
* Strongly instead of loosely typed results.
* Support for parameters.
* ...
Do we want to support an index date (i.e., avoid assuming
today's date)?
Don't we want to forego tokens? Or are we assuming that
consumers of
logic know which rule provider to consult (which may or may
not use
tokens)? If the consumer needs to know which provider to
consult and
that provider will presumably be handling the evaluation, then
what
purpose does the logic service serve? Alternatively, rule
providers
could be registered with the logic service and then consumers
could
come to the logic service with a token (i.e., not need to know the
provider).
I'm assuming that we'll defer caching & criteria
implementations to
the various forms of logic (at least for now).
What's the purpose of LogicContext now? In the original design,
LogicContext served a few purposes: (1) proxy for all data
requests
by rules, (2) provide a context for evaluations ? index date &
parameters ? that could be stacked for recursion, (3) context for
caching results, and (4) an abstraction so that the same rule
could
be run against a patient or cohort. If we aren't using logic
context
for any of these, do we need to maintain LogicContext at this
stage?
Looking forward to the design call.
Cheers,
-Burke
On Wed, Nov 9, 2011 at 10:23 PM, Michael Seaton
<[email protected]
<mailto:[email protected]><mailto:[email protected]
<mailto:[email protected]>>> wrote:
Hi all,
I wanted to pick up a thread on the future of Logic that we spent
some time discussing during the Developers Forum on October
27<https://wiki.openmrs.org/x/5oamAQ>.
The overall consensus on the call was that "Logic is too
complicated.
We wish there were a simpler implementation, that were easier to
adapt to individual needs. We wish it were more rigorously
tested,
including performance testing".
Anyone that cares about preserving Logic as it currently
exists, or
wants to see it change in a specific way, please read further
Specific users of Logic have communicated the following needs:
* Tammy and her team require more control over the current
Logic
implementation, and better testing around it, so that future
upgrades
do not cause serious bugs and performance degradation
* Win and Tammy need implementations of Logic that are
optimized
for running lots of rules for individual patients at a time
* Mike needs an implementation of Logic that is optimized for
running a single rule for lots of patients at a time as well
* Mike wants to be able to choose to not use the existing Logic
implementation in favor of an implementation provided by the
Reporting module
To accomplish this, the following high-level refactoring steps are
being considered:
* Reduce and simplify the number of interfaces and interface
methods that Logic exposes in OpenMRS core
* De-couple the existing Logic Module in such a way that it
is one
of many possible "Rule Providers" that can plug into this
lighter-weight core framework
* Remove, if possible and practical, the need to have a
"required"
Logic module installed
Specifically, we are considering an approach that would reduce the
core Logic interfaces to something like:
interface LogicContext;
interface Result; (classes DateResult, NumberResult,
ListResult...)
interface Rule {
Set<RuleParameterInfo> getParameterInfo();
Class<? extends Result> getReturnType();
Result evaluate(Integer patientId, Map<String, Object>
parameters,
LogicContext context);
}
interface LogicService {
public Result evaluate(Rule rule, Patient patient, Map<String,
Object> parameters, LogicContext context);
public Map<Integer, Result> evaluate(Rule rule, Cohort cohort,
Map<String, Object> parameters, LogicContext context);
}
I would like for this to spur the following potential activities:
* Give anyone who has not yet been aware of these
discussions, or
who does not agree with this approach, an opportunity to weigh
in and
get involved in the process
* Make a plan to put this topic onto one or more future Design
Forum calls, in order to agree upon the revised design, make
tickets,
and establish an owner for moving it forward.
* Publicize when this Design Forum will occur so that
interested
parties can be involved as desired
Thanks!
Mike
________________________________
Click here to
unsubscribe<mailto:[email protected]
<mailto:[email protected]>?body=SIGNOFF%20openmrs-devel-l>
from OpenMRS Developers' mailing
list
________________________________
Click here to
unsubscribe<mailto:[email protected]
<mailto:[email protected]>?body=SIGNOFF%20openmrs-devel-l>
from OpenMRS Developers' mailing
list
________________________________
Click here to
unsubscribe<mailto:[email protected]
<mailto:[email protected]>?body=SIGNOFF%20openmrs-devel-l>
from OpenMRS Developers' mailing
list
_________________________________________
To unsubscribe from OpenMRS Developers' mailing list, send an
e-mail
to [email protected]
<mailto:[email protected]> with "SIGNOFF
openmrs-devel-l" in the
body (not the subject) of your e-mail.
[mailto:[email protected]
<mailto:[email protected]>?body=SIGNOFF%20openmrs-devel-l]
_________________________________________
To unsubscribe from OpenMRS Developers' mailing list, send an
e-mail to [email protected]
<mailto:[email protected]> with "SIGNOFF
openmrs-devel-l" in the body (not the subject) of your e-mail.
[mailto:[email protected]
<mailto:[email protected]>?body=SIGNOFF%20openmrs-devel-l]
------------------------------------------------------------------------
Click here to unsubscribe
<mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l>
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]