Re: JESS: Jess Access Control
On Jan 7, 2011, at 8:06 PM, Michael Eugene Artz wrote: It's a casting error but it doesn't make any sense to me why I would be getting that. userProfiles is a java.util.Set. The method Set.add() returns boolean (true means the add() succeeded, false means the object was already in the set.) So here you're mapping Integer to Boolean. profiles.put(new Integer(1), userProfiles.add(new Profile(1, "Mike", "Artz"))); profiles.put(new Integer(2), userProfiles.add(new Profile(2, "Suzy", "Kolber"))); profiles.put(new Integer(3), userProfiles.add(new Profile(3, "Stan", "Mikita"))); profiles.put(new Integer(4), userProfiles.add(new Profile(4, "Nikita", "Kruschev"))); Here you're assuming that the values in profiles are Profile objects, but as we said, they're Booleans. public Profile getProfile(int profileNumber) { //return new Profile(1, "Mike", "Artz"); return (Profile)profiles.get(new Integer(profileNumber)); } Which is what the error says: the values are Booleans, not Profiles. The trace tells you exactly what line causes the errors, so a println() or two would have easily told you the whole story. java.lang.ClassCastException: java.lang.Boolean cannot be cast to Profile at DemoDatabase.getProfile(DemoDatabase.java:49) at Demo.processRole(Demo.java:31) at Demo.main(Demo.java:13) I wonder if I could this all much simpler and Im just wasting my time completely:( When I assemble a large program like this, I start with a small program that does one little thing, and I test it. I then add a little more, and test that, and so on. At each step, I make sure all the new code does what it's supposed to do. I keep all the test code so it can be run at any time to verify that the program still works. That way, you never find yourself looking at a big pile of code and wondering where the problem is: the problem is always just in that last little bit you just coded. Good luck! - Ernest Friedman-Hill Informatics & Decision Sciences Phone: (925) 294-2154 Sandia National Labs PO Box 969, MS 9012ejfr...@sandia.gov Livermore, CA 94550 http://www.jessrules.com To unsubscribe, send the words 'unsubscribe jess-users y...@address.com' in the BODY of a message to majord...@sandia.gov, NOT to the list (use your own address!) List problems? Notify owner-jess-us...@sandia.gov.
JESS: Jess Access Control
Hi, Im trying to do a simple Access control program using Jess. Everything pretty much shadows the Pricing example that comes with Jess. I changed only a few variable names and modified a couple of the parts that weren't need, because mine should be actually simpler. All I want to do is have a handfule of rules that check for a user info with respect to a certain file and decides whether they have read-write priveledges or just read priveledges. And also to assign read-write priveledges to users if they are owners of the respected file that they want to access. I am sorry if its just some simple error, but I have been stumped on this for some time, so I don't know what to do. Right now my Java classes are in seperate files in the same package, but Ill just cut and paste them all here so it will be easier to look at them. The file after all the JAva files is my .clp JEss file, then after that is the error I'm getting. It's a casting error but it doesn't make any sense to me why I would be getting that. Anyway , they look like: import java.util.ArrayList; import java.util.Collection; public interface Database { public Collection getFiles(); public Profile getProfile(int profileNumber); } import java.util.Iterator; import jess.JessException; public class Demo { public static void main(String[] args) { try { DemoDatabase database = new DemoDatabase(); UserAccessEngine engine = new UserAccessEngine(database); processRole(database, engine, 1); processRole(database, engine, 2); processRole(database, engine, 3); processRole(database, engine, 4); } catch (JessException e) { e.printStackTrace(); } } private static void processRole(DemoDatabase database, UserAccessEngine engine, int aID) throws JessException { //Iterator files, prints out a message prior to getting the needed info Iterator roles; System.out.println("First and Last Name of User " + aID + ":"); String firstName = database.getProfile(aID).getfirst(); String lastName = database.getProfile(aID).getlast(); System.out.println(firstName + " " + lastName); roles = engine.run(aID); System.out.println("Roles for " + firstName + ":"); while (roles.hasNext()) { System.out.println(" " + roles.next()); } System.out.println(); } } import java.util.ArrayList; import java.util.Map; import java.util.Collection; import java.util.HashMap; /** * A toy implementation of the Database interface with some * hard-coded file and profile data. */ public class DemoDatabase implements Database { private ArrayList files; private Profile profile; private Map profiles; public DemoDatabase() { createFiles(); createProfiles(); } private void createProfiles() { profiles = new HashMap(); ArrayList userProfiles = new ArrayList(); profiles.put(new Integer(1), userProfiles.add(new Profile(1, "Mike", "Artz"))); profiles.put(new Integer(2), userProfiles.add(new Profile(2, "Suzy", "Kolber"))); profiles.put(new Integer(3), userProfiles.add(new Profile(3, "Stan", "Mikita"))); profiles.put(new Integer(4), userProfiles.add(new Profile(4, "Nikita", "Kruschev"))); } private void createFiles() { files = new ArrayList(); files.add(new FileRead("Mike", "Artz", "file.txt", 1)); files.add(new FileRead("Tom", "Saywer", "picOfYourMom.jpg", 5)); files.add(new FileRead("Chris", "Webber", "NBAJAMS.exe", 6)); } public Collection getFiles() { return files; } public Profile getProfile(int profileNumber) { //return new Profile(1, "Mike", "Artz"); return (Profile)profiles.get(new Integer(profileNumber)); } } public class FilePermission { private String fileName; private int profileNum; private String last; private String first; private String role; public FilePermission(String aFileName, int aProfileNum, String aLast, String aFirst, String aRole) { last = aLast; first = aFirst; fileName = aFileName; profileNum = aProfileNum; role = aRole; } public String getfileName() { return fileName; } public int getprofileNum() { return profileNum; } public String getlast() { return last; } public String getfirst() { return first; } public String getrole() { return role; } } public class FileRead { private String ownerFirst; private String ownerLast; private String file; private int ownerID; public FileRead(String aFirst, String aLast, String aFile, int aOwnerID) { ownerFirst = aLast; ownerLast = aFirst; file = aFile; ownerID = aOwnerID; } public String getownerFirst() { return ownerFirst; } public String getownerLast() { return ownerLast; } public String getFile() { return file; } public int getownerID() { return ownerID; } } public class Profile { private String last; private String first; private int idNum; public Profile(int aID, String aFirst, String aLast) { idNum = aID; last = aLast; first = aFirst; } public String getlast() { return last; } public String getfirst() { return first; } public int getidNum() { return
RE: JESS: Re: calculating benefit costs
Agree with Peter, And, there might be another solution (others are already mentioned by Wolfgang), which I believe, is a normal practice for other rule-engines available in the current market, providing the concept of Decision Table/Matrix. So, please do the following exercise before writing the rules in JESS language - 1.) Represent this rule into excel sheet as decision table/matrix format (combination of conditions/actions). 2.) Try to find the match in conditions block, and merge it first if there is match in condition(s), so that you can easily find the gaps and duplicates (if there is any). 3.) Try to find how you can normalize this single big table into multiples (if possible), i.e. one table might contain few combined conditions and one or minimum actions, and the condition values could be derived/inferred from other decision table/matrics. Now, once done, 1.) Consider each column of a decision table/matrix as one single rule. As mentioned in #3, you can use JESS *saliance* concept to each rule to set the priority relative to all other rule(s)/rulesets, and using this concept, the less priority rules get the *inferred result* as input from the prioritized ones. 2.) Please write all the combined *if* conditions into the LHS side of each rule. 3.) The RHS will include only the call methods which is setting some value to an object (as per the example), and the inferred result will be input for other rules having less priority. Using this concept, you might have a big list of rules, which is easily understandable/maintainable from user/developer point of view. But still one BIG question in my mind, compare to Wolfgang example or in general, is this really a better approach from rule-engine working memory (or any other) perspective? Regards DEBASISH DALUI (122816) -- Cognizant Technology Solutions US Corp Cell : +1-216-835-2902 From: owner-jess-us...@sandia.gov on behalf of Peter Lin Sent: Fri 1/7/2011 11:58 AM To: jess-users@sandia.gov Subject: Re: JESS: Re: calculating benefit costs I couldn't agree more with james and donald. Having a ton of if/then/else statements in the RHS really should be avoided. One of the benefits of using a rule-centric approach is it helps you see the logic. I find that having deeply nested if/then/else often hides logical flaws that are hard to fix and debug. Think of it like having small simple methods in your java code versus having a gigantic java method with thousands of lines. On Fri, Jan 7, 2011 at 10:35 AM, Donald Paul Winston wrote: > So many if else statements on the RHS is "unruly". > On Jan 7, 2011, at 9:15 AM, Derek Adams wrote: > > Thanks for the help guys. Here is what I ended up with. This works, but > I'm sure it's not the most efficient way to solve the problem. > > (defrule setCalculatedCostGCI20k5k > (HrBenefitJoin (hrBenefitConfigId "1-76")(benefitJoinId > ?bjid)(calculatedCost ?cost)(OBJECT ?obj)(coveredPersonId > ?cPer)(payingPersonId ?pPer)(relationshipId ?rel)) > (Person (personId ?cPer)(dob ?dob)(OBJECT ?objP)) > => > (printout t "age = " (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) " for " ?bjid " " crlf) > > (if (eq ?rel nil) then > (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now > )) 30) then > (call ?obj overrideAgeCost 11.55) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 40) then > (call ?obj overrideAgeCost 18.95) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 50) then > (call ?obj overrideAgeCost 38.35) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 60) then > (call ?obj overrideAgeCost 65.95) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 70) then > (call ?obj overrideAgeCost 104.35) > else > (printout t "age greater than 69 ... " > (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) " for " > ?bjid " " crlf)) > else (if (neq ?rel nil) then > (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 30) then > (call ?obj overrideAgeCost 4.20) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 40) then > (call ?obj overrideAgeCost 6.05) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 50) then > (call ?obj overrideAgeCost 10.90) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 60) then >
Re: JESS: Re: calculating benefit costs
Hi Derek, OK OK... I wasn't going to say anything either but with Peter and James added to Wolfgang, I have to pile too :-) IMHO there are a number of maxims of rule-based programming that you're breaking here. Ernest has put the first one most succinctly in the past: Use many smaller rules that do one thing well rather than one Über Rule that boils the ocean. Declarative programming should say what action(s) are to be performed when certain facts are present, but not attempt to implement those actions directly on the RHS. The second, as James and Peter pointed out, is not to do Java programming on the RHS. If you make the RHS a series of method calls that take the variable bindings from the LHS as arguments, your rules will be much cleaner and maintainable. Finally -- and this is just a plain programming nit -- I noticed the apparent use of magic numbers in your conditions. What about all these calls to overrideAgeCost()? Where are those args coming from? Are they part of some policy? What if that policy changes? Then your rules would be in a "dirty" form. Wouldn't it be better to get those values from some cache or database with most current values? Something that might help you: I've been collecting rule-based *metaphors* lately -- different ways of thinking about using rules. One metaphor that has been particularly productive has been thinking about "digestion" and the passing of data through a series of modules like a "digestive tract". Obviously, if you carry the metaphor too far ... well you see where the "garbage in/ garbage out" saying comes into play. :-) But the idea is that you are moving the data through different states, partially processing it each time a new rule module has a crack at it. This has precedent in UNIX/LINUX with pipes -- same idea. Old wine in a new bottle? Perhaps. But this way, you can clearly separate out concerns, add pre and post processing functions, and test partial results by simply disabling/enabling certain modules in the sequence. Cheers, Jason -- Jason Morris Chairman, Rules Fest 2010/2011 http://www.rulesfest.org Morris Technical Solutions LLC consult...@morris-technical-solutions.com (517) 304-5883
Re: JESS: Rule firing during execution of RHS
It is atomic with respect to other rules firing; only one rule can be firing at a time. It's not atomic with respect to working memory changes, though; working memory can be changed by other threads while the RHS of a rule is executing. On Jan 7, 2011, at 12:47 PM, Nguyen, Son wrote: Hi, Execution of a rule RHS is sequential. But is it atomic? In other words, during that time, is it possible that other activated rules get fired? These activated rules include those that had been on the agenda or newly activated rules due to pattern matching caused by the RHS invocation of modify, assert or retract. Any feedback is greately appreciated. Son Nguyen - Ernest Friedman-Hill Informatics & Decision Sciences, Sandia National Laboratories PO Box 969, MS 9012, Livermore, CA 94550 http://www.jessrules.com To unsubscribe, send the words 'unsubscribe jess-users y...@address.com' in the BODY of a message to majord...@sandia.gov, NOT to the list (use your own address!) List problems? Notify owner-jess-us...@sandia.gov.
JESS: Rule firing during execution of RHS
Hi, Execution of a rule RHS is sequential. But is it atomic? In other words, during that time, is it possible that other activated rules get fired? These activated rules include those that had been on the agenda or newly activated rules due to pattern matching caused by the RHS invocation of modify, assert or retract. Any feedback is greately appreciated. Son Nguyen
Re: JESS: Re: calculating benefit costs
I couldn't agree more with james and donald. Having a ton of if/then/else statements in the RHS really should be avoided. One of the benefits of using a rule-centric approach is it helps you see the logic. I find that having deeply nested if/then/else often hides logical flaws that are hard to fix and debug. Think of it like having small simple methods in your java code versus having a gigantic java method with thousands of lines. On Fri, Jan 7, 2011 at 10:35 AM, Donald Paul Winston wrote: > So many if else statements on the RHS is "unruly". > On Jan 7, 2011, at 9:15 AM, Derek Adams wrote: > > Thanks for the help guys. Here is what I ended up with. This works, but > I'm sure it's not the most efficient way to solve the problem. > > (defrule setCalculatedCostGCI20k5k > (HrBenefitJoin (hrBenefitConfigId "1-76")(benefitJoinId > ?bjid)(calculatedCost ?cost)(OBJECT ?obj)(coveredPersonId > ?cPer)(payingPersonId ?pPer)(relationshipId ?rel)) > (Person (personId ?cPer)(dob ?dob)(OBJECT ?objP)) > => > (printout t "age = " (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) " for " ?bjid " " crlf) > > (if (eq ?rel nil) then > (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now > )) 30) then > (call ?obj overrideAgeCost 11.55) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 40) then > (call ?obj overrideAgeCost 18.95) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 50) then > (call ?obj overrideAgeCost 38.35) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 60) then > (call ?obj overrideAgeCost 65.95) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 70) then > (call ?obj overrideAgeCost 104.35) > else > (printout t "age greater than 69 ... " > (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) " for " > ?bjid " " crlf)) > else (if (neq ?rel nil) then > (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 30) then > (call ?obj overrideAgeCost 4.20) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 40) then > (call ?obj overrideAgeCost 6.05) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 50) then > (call ?obj overrideAgeCost 10.90) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 60) then > (call ?obj overrideAgeCost 17.80) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 70) then > (call ?obj overrideAgeCost > 27.40) > else > (printout t "age greater than 69 > ... " (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) " for > " ?bjid " " crlf > (printout t "setCalculatedCostGCI function fired for " ?bjid ". > overrideAgeCost = " (call ?obj overrideAgeCost) crlf) > ;(printout t "setCalculatedCostGCI function fired for " ?bjid " " crlf) > > ) > > On Thu, Jan 6, 2011 at 10:32 AM, Wolfgang Laun > wrote: >> >> This additional information does not require a fundamentally different >> approach. >> >> Using the same set of facts CostEmployee/CostSpouse, we can now use >> two rules, one calculating the cost for the employee, and the other >> one for the spouse. >> >> (defrule calc-self >> ?hbj <- (HrBenefitJoin (hrBenefitConfigId "1-73") >> (calculatedCost 0) >> {relationshipId == nil} >> (payingPersonId ?pPer) >> (coveredPersonId ?cPer) ) >> (Person(personId?cPer) (dob ?dob) ) >> (CostEmployee (lo ?lo&:(<= ?lo (yrs ?dob))) >> (hi ?hi&:(>= ?hi (yrs ?dob)))(cost ?cost)) >> => >> (printout t "Cost for " ?cPer " paid by himself: " ?cost crlf ) >> ) >> >> (defrule calc-other >> ?hbj <- (HrBenefitJoin (hrBenefitConfigId "1-73") >> (calculatedCost 0) >> {relationshipId != nil} >> (payingPersonId ?pPer) >> (coveredPersonId ?cPer) ) >> (Person(personId?cPer) (dob ?dob) ) >> (CostSpouse(lo ?lo&:(<= ?lo (yrs ?dob))) >> (hi ?hi&:(>= ?hi (yrs ?dob)))(cost ?cost)) >> => >> (printout t "Cost for " ?cPer " paid by " ?pPer ": " ?cost crlf ) >> ) >> >> The date calculation is abstracted into a >> (deffunction yrs (?dob) ... (return ?yrs) ) >> >>
Re: JESS: Re: calculating benefit costs
So many if else statements on the RHS is "unruly". On Jan 7, 2011, at 9:15 AM, Derek Adams wrote: > Thanks for the help guys. Here is what I ended up with. This works, but I'm > sure it's not the most efficient way to solve the problem. > > (defrule setCalculatedCostGCI20k5k > (HrBenefitJoin (hrBenefitConfigId "1-76")(benefitJoinId > ?bjid)(calculatedCost ?cost)(OBJECT ?obj)(coveredPersonId > ?cPer)(payingPersonId ?pPer)(relationshipId ?rel)) > (Person (personId ?cPer)(dob ?dob)(OBJECT ?objP)) > => > (printout t "age = " (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) " for " ?bjid " " crlf) > > (if (eq ?rel nil) then > (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now > )) 30) then > (call ?obj overrideAgeCost 11.55) > else (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils > now )) 40) then > (call ?obj overrideAgeCost 18.95) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 50) then > (call ?obj overrideAgeCost 38.35) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 60) then > (call ?obj overrideAgeCost 65.95) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 70) then > (call ?obj overrideAgeCost 104.35) > else > (printout t "age greater than 69 ... " > (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) " for " ?bjid > " " crlf)) > else (if (neq ?rel nil) then > (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 30) then > (call ?obj overrideAgeCost 4.20) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 40) then > (call ?obj overrideAgeCost 6.05) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 50) then > (call ?obj overrideAgeCost 10.90) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 60) then > (call ?obj overrideAgeCost 17.80) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 70) then > (call ?obj overrideAgeCost 27.40) > else > (printout t "age greater than 69 > ... " (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) " for " > ?bjid " " crlf > (printout t "setCalculatedCostGCI function fired for " ?bjid ". > overrideAgeCost = " (call ?obj overrideAgeCost) crlf) > ;(printout t "setCalculatedCostGCI function fired for " ?bjid " " crlf) > > ) > > On Thu, Jan 6, 2011 at 10:32 AM, Wolfgang Laun > wrote: > This additional information does not require a fundamentally different > approach. > > Using the same set of facts CostEmployee/CostSpouse, we can now use > two rules, one calculating the cost for the employee, and the other > one for the spouse. > > (defrule calc-self > ?hbj <- (HrBenefitJoin (hrBenefitConfigId "1-73") > (calculatedCost 0) > {relationshipId == nil} > (payingPersonId ?pPer) > (coveredPersonId ?cPer) ) > (Person(personId?cPer) (dob ?dob) ) > (CostEmployee (lo ?lo&:(<= ?lo (yrs ?dob))) > (hi ?hi&:(>= ?hi (yrs ?dob)))(cost ?cost)) > => > (printout t "Cost for " ?cPer " paid by himself: " ?cost crlf ) > ) > > (defrule calc-other > ?hbj <- (HrBenefitJoin (hrBenefitConfigId "1-73") > (calculatedCost 0) > {relationshipId != nil} > (payingPersonId ?pPer) > (coveredPersonId ?cPer) ) > (Person(personId?cPer) (dob ?dob) ) > (CostSpouse(lo ?lo&:(<= ?lo (yrs ?dob))) > (hi ?hi&:(>= ?hi (yrs ?dob)))(cost ?cost)) > => > (printout t "Cost for " ?cPer " paid by " ?pPer ": " ?cost crlf ) > ) > > The date calculation is abstracted into a > (deffunction yrs (?dob) ... (return ?yrs) ) > > Implementing this in Jess or in Java is a simple programming exercise, > but it presumably it depends on some technical/legal issues, e.g., it > may not be possible to use "today" as a basis for calculating the > difference in years from the person's dob. > > Since there's no information what to do with the resulting cost, it's > just printed to standard output, but updating slot calculatedCost in > HrBenefitJoin is straightforward. > > Cheers > Wolfgang > > > On 05/01/2011, Derek Adams wrote: > > the f
Re: JESS: Re: calculating benefit costs
Although most folks won't complain, my only comment here is that the logic is all contained in the action side (RHS) of the rule in the form of if-then-else statements (basic Java) followed by more if-then-else (more basic Java) statements. This is basically procedural code put into a rulebase in a procedural manner. This begs the question, "Is a rulebase actually needed for this problem?" Not the way is has been solved here. I kind of like Wolfgang's original solution on 5 Jan which would be more of a rulebase approach and would make maintenance at a later date much easier. Yes, it has more rules (which is the object of a rulebase), BUT the logic is properly defined on the LHS where it belongs and each rule is independently incremental as it should be. Also, using the rulebase approach would allow expansion of the basic problem into more complex problems later on. SDG jco On Jan 7, 2011, at 8:15 AM, Derek Adams wrote: > Thanks for the help guys. Here is what I ended up with. This works, but I'm > sure it's not the most efficient way to solve the problem. > > (defrule setCalculatedCostGCI20k5k > (HrBenefitJoin (hrBenefitConfigId "1-76")(benefitJoinId > ?bjid)(calculatedCost ?cost)(OBJECT ?obj)(coveredPersonId > ?cPer)(payingPersonId ?pPer)(relationshipId ?rel)) > (Person (personId ?cPer)(dob ?dob)(OBJECT ?objP)) > => > (printout t "age = " (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) " for " ?bjid " " crlf) > > (if (eq ?rel nil) then > (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now > )) 30) then > (call ?obj overrideAgeCost 11.55) > else (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils > now )) 40) then > (call ?obj overrideAgeCost 18.95) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 50) then > (call ?obj overrideAgeCost 38.35) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 60) then > (call ?obj overrideAgeCost 65.95) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 70) then > (call ?obj overrideAgeCost 104.35) > else > (printout t "age greater than 69 ... " > (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) " for " ?bjid > " " crlf)) > else (if (neq ?rel nil) then > (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 30) then > (call ?obj overrideAgeCost 4.20) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 40) then > (call ?obj overrideAgeCost 6.05) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 50) then > (call ?obj overrideAgeCost 10.90) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 60) then > (call ?obj overrideAgeCost 17.80) > else (if (< (call ?objP calcAgeAsOf(call > com.arahant.utils.DateUtils now )) 70) then > (call ?obj overrideAgeCost 27.40) > else > (printout t "age greater than 69 > ... " (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) " for " > ?bjid " " crlf > (printout t "setCalculatedCostGCI function fired for " ?bjid ". > overrideAgeCost = " (call ?obj overrideAgeCost) crlf) > ;(printout t "setCalculatedCostGCI function fired for " ?bjid " " crlf) > > ) > > On Thu, Jan 6, 2011 at 10:32 AM, Wolfgang Laun > wrote: > This additional information does not require a fundamentally different > approach. > > Using the same set of facts CostEmployee/CostSpouse, we can now use > two rules, one calculating the cost for the employee, and the other > one for the spouse. > > (defrule calc-self > ?hbj <- (HrBenefitJoin (hrBenefitConfigId "1-73") > (calculatedCost 0) > {relationshipId == nil} > (payingPersonId ?pPer) > (coveredPersonId ?cPer) ) > (Person(personId?cPer) (dob ?dob) ) > (CostEmployee (lo ?lo&:(<= ?lo (yrs ?dob))) > (hi ?hi&:(>= ?hi (yrs ?dob)))(cost ?cost)) > => > (printout t "Cost for " ?cPer " paid by himself: " ?cost crlf ) > ) > > (defrule calc-other > ?hbj <- (HrBenefitJoin (hrBenefitConfigId "1-73") > (calculatedCost 0) > {relationshipId != nil} > (payingPersonId ?pPer) > (coveredPersonId ?cPer) )
Re: JESS: Re: calculating benefit costs
Thanks for the help guys. Here is what I ended up with. This works, but I'm sure it's not the most efficient way to solve the problem. (defrule setCalculatedCostGCI20k5k (HrBenefitJoin (hrBenefitConfigId "1-76")(benefitJoinId ?bjid)(calculatedCost ?cost)(OBJECT ?obj)(coveredPersonId ?cPer)(payingPersonId ?pPer)(relationshipId ?rel)) (Person (personId ?cPer)(dob ?dob)(OBJECT ?objP)) => (printout t "age = " (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) " for " ?bjid " " crlf) (if (eq ?rel nil) then (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) 30) then (call ?obj overrideAgeCost 11.55) else (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) 40) then (call ?obj overrideAgeCost 18.95) else (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) 50) then (call ?obj overrideAgeCost 38.35) else (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) 60) then (call ?obj overrideAgeCost 65.95) else (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) 70) then (call ?obj overrideAgeCost 104.35) else (printout t "age greater than 69 ... " (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) " for " ?bjid " " crlf)) else (if (neq ?rel nil) then (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) 30) then (call ?obj overrideAgeCost 4.20) else (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) 40) then (call ?obj overrideAgeCost 6.05) else (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) 50) then (call ?obj overrideAgeCost 10.90) else (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) 60) then (call ?obj overrideAgeCost 17.80) else (if (< (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) 70) then (call ?obj overrideAgeCost 27.40) else (printout t "age greater than 69 ... " (call ?objP calcAgeAsOf(call com.arahant.utils.DateUtils now )) " for " ?bjid " " crlf (printout t "setCalculatedCostGCI function fired for " ?bjid ". overrideAgeCost = " (call ?obj overrideAgeCost) crlf) ;(printout t "setCalculatedCostGCI function fired for " ?bjid " " crlf) ) On Thu, Jan 6, 2011 at 10:32 AM, Wolfgang Laun wrote: > This additional information does not require a fundamentally different > approach. > > Using the same set of facts CostEmployee/CostSpouse, we can now use > two rules, one calculating the cost for the employee, and the other > one for the spouse. > > (defrule calc-self > ?hbj <- (HrBenefitJoin (hrBenefitConfigId "1-73") > (calculatedCost 0) > {relationshipId == nil} > (payingPersonId ?pPer) > (coveredPersonId ?cPer) ) > (Person(personId?cPer) (dob ?dob) ) > (CostEmployee (lo ?lo&:(<= ?lo (yrs ?dob))) > (hi ?hi&:(>= ?hi (yrs ?dob)))(cost ?cost)) > => > (printout t "Cost for " ?cPer " paid by himself: " ?cost crlf ) > ) > > (defrule calc-other > ?hbj <- (HrBenefitJoin (hrBenefitConfigId "1-73") > (calculatedCost 0) > {relationshipId != nil} > (payingPersonId ?pPer) > (coveredPersonId ?cPer) ) > (Person(personId?cPer) (dob ?dob) ) > (CostSpouse(lo ?lo&:(<= ?lo (yrs ?dob))) > (hi ?hi&:(>= ?hi (yrs ?dob)))(cost ?cost)) > => > (printout t "Cost for " ?cPer " paid by " ?pPer ": " ?cost crlf ) > ) > > The date calculation is abstracted into a > (deffunction yrs (?dob) ... (return ?yrs) ) > > Implementing this in Jess or in Java is a simple programming exercise, > but it presumably it depends on some technical/legal issues, e.g., it > may not be possible to use "today" as a basis for calculating the > difference in years from the person's dob. > > Since there's no information what to do with the resulting cost, it's > just printed to standard output, but updating slot calculatedCost in > HrBenefitJoin is straightforward. > > Cheers > Wolfgang > > > On 05/01/2011, Derek Adams wrote: > > the facts that I am working with are: > > > > (HrBenefitJoin (hrBenefitConfigId "1-73")(benefitJoinId > > ?bjid)(calculatedCost ?cost)(OBJECT ?obj)(coveredPersonId > > ?cPer)(payingPersonId ?pPer)(relationshipId ?rel)) > > (Person (personId ?cP
JESS: Re: calculating benefit costs
This additional information does not require a fundamentally different approach. Using the same set of facts CostEmployee/CostSpouse, we can now use two rules, one calculating the cost for the employee, and the other one for the spouse. (defrule calc-self ?hbj <- (HrBenefitJoin (hrBenefitConfigId "1-73") (calculatedCost 0) {relationshipId == nil} (payingPersonId ?pPer) (coveredPersonId ?cPer) ) (Person(personId?cPer) (dob ?dob) ) (CostEmployee (lo ?lo&:(<= ?lo (yrs ?dob))) (hi ?hi&:(>= ?hi (yrs ?dob)))(cost ?cost)) => (printout t "Cost for " ?cPer " paid by himself: " ?cost crlf ) ) (defrule calc-other ?hbj <- (HrBenefitJoin (hrBenefitConfigId "1-73") (calculatedCost 0) {relationshipId != nil} (payingPersonId ?pPer) (coveredPersonId ?cPer) ) (Person(personId?cPer) (dob ?dob) ) (CostSpouse(lo ?lo&:(<= ?lo (yrs ?dob))) (hi ?hi&:(>= ?hi (yrs ?dob)))(cost ?cost)) => (printout t "Cost for " ?cPer " paid by " ?pPer ": " ?cost crlf ) ) The date calculation is abstracted into a (deffunction yrs (?dob) ... (return ?yrs) ) Implementing this in Jess or in Java is a simple programming exercise, but it presumably it depends on some technical/legal issues, e.g., it may not be possible to use "today" as a basis for calculating the difference in years from the person's dob. Since there's no information what to do with the resulting cost, it's just printed to standard output, but updating slot calculatedCost in HrBenefitJoin is straightforward. Cheers Wolfgang On 05/01/2011, Derek Adams wrote: > the facts that I am working with are: > > (HrBenefitJoin (hrBenefitConfigId "1-73")(benefitJoinId > ?bjid)(calculatedCost ?cost)(OBJECT ?obj)(coveredPersonId > ?cPer)(payingPersonId ?pPer)(relationshipId ?rel)) > (Person (personId ?cPer)(dob ?dob)(OBJECT ?objP)) > > ?rel is what identifies the "covered person" as spouse or employee. If ?rel > is nil, it's employee, else it's spouse. > > So ?objP is my covered person (either spouse or employee). > > On Wed, Jan 5, 2011 at 3:36 PM, Wolfgang Laun > wrote: > >> With this kind of problem, there are (at least) two approaches. Given that >> Employee and Spouse are represented as facts: >>(deftemplate Spouse (slot name)(slot age)) >>(deftemplate Employee (slot name)(slot age)(slot spouse)) >> >>(deffacts test-facts >> (Employee (name "John Smith")(age 35)(spouse "Jane Smith")) >> (Spouse (name "Jane Smith")(age 28)) >> (Employee (name "Honey Rider")(age 39)(spouse "James Rider")) >> (Spouse (name "James Rider")(age 45)) >>) >> >> (1) You can write one rule for each possible combination of age brackets. >> >> (defrule compute_1_1 >> (Employee (name ?name){age >= 18 && age <= 29}(spouse ?spouse)) >> (Spouse {name == ?spouse}{age >= 18 && age <= 29}) >> => >> (printout t "cost " ?name " " (+ 11.55 6.65) crlf) >> ) >> ... more boring code ... >> (defrule compute_3_3 >> (Employee (name ?name){age >= 40 && age <= 49}(spouse ?spouse)) >> (Spouse {name == ?spouse}{age >= 40 && age <= 49}) >> => >> (printout t "cost " ?name " " (+ 38.35 20.05) crlf) >> ) >> >> (2) You can represent the tables for the costs as facts: >>(deftemplate CostEmployee (slot lo)(slot hi)(slot cost)) >>(deftemplate CostSpouse (slot lo)(slot hi)(slot cost)) >> >>(deffacts costs >>(CostEmployee (lo 18)(hi 29)(cost 11.55)) >>(CostEmployee (lo 30)(hi 39)(cost 18.95)) >>(CostEmployee (lo 40)(hi 49)(cost 38.35)) >>(CostSpouse (lo 18)(hi 29)(cost 6.65)) >>(CostSpouse (lo 30)(hi 39)(cost 10.35)) >>(CostSpouse (lo 40)(hi 49)(cost 20.05)) >>) >> >> and use a single rule: >> >>(defrule compute >>(Employee (name ?name)(age ?ageE)(spouse ?spouse)) >>(Spouse {name == ?spouse}(age ?ageS)) >>(CostEmployee {lo <= ?ageE}{hi >= ?ageE}(cost ?costE)) >>(CostSpouse {lo <= ?ageS}{hi >= ?ageS}(cost ?costS)) >>=> >>(printout t "cost " ?name " " (+ ?costE ?costS) crlf) >>) >> >> HTH >> Wolfgang >> >> >> >> >> >> On 5 January 2011 22:30, Wolfgang Laun wrote: >> >>> Sent on behalf of Derek Adams: >>> >>> >>> Hey Jess Users! This is my first post, so I apologize for anything >>> "noob" >>> that I say/do... >>> >>> I'm very new to Jess. I understand the basics but applying my knowledge >>> for the first time is proving more difficult than I anticipated. Here is >>> the problem: >>> >>> I need to set up a rule in Jess that calculates the cost of a benefit >>> based on age and enrollees. >>> * >>> Example: >>> The Benefit Configuration is "Employee $10k / Spouse $5k" >>> The enrollees are the employee (John Smith age 30) and spouse (Jane Smith >>> and 28). >>> The costs are calc