Working with consecutive events in drools - drools

I need help with the following rule:
Accounts with multiple consecutive deposits and immediately after 4 hours, multiple extractions whose sum is equal to the same amount deposited using different ATM in the same bank.
(There aren't intermediate operations between the last deposit and the first extraction)
I have the class Operation: Account, Amount, Operation, ATM, Timestamp.
I have this rule:
declare Operation
#role(event)
#timestamp(Timestamp)
end
rule
when
$op1:Operation($acc:Account,$b:Bank,$amount:Amount,Operation‌​=="CREDIT",$atm:ATM)
not Operation(Account == $acc,ATM != $atm, Bank == $b, this before $op1) accumulate($op2:Operation(Account == $acc,ATM != $atm, Bank == $b,Operation=="CREDIT",$amount1:Amount);$c:count($op2),$d:su‌​m($amount1);$c>=4,$d‌​>=300)
then
System.out.println( "Account: " + $acc + " amount " + ($amount + $d) + " operations " + $c+ " in different banks");
end
The previous rule returns the sum of the deposits of the accounts that have made more than 4 operations in different ATMs of the same bank,my doubt is how to take into account that they are consecutive and that after 4 hours compare the previous sum with the sum of the extractions from that account.

If this rule fires,
rule "set up Monitor"
when
$op1: Operation( $a:account, $b:bank, $amt1:amount, operation‌​=="CREDIT", $atm1: atm )
$op2: Operation( account==$a, bank==$b, $amt2:amount, operation‌​=="CREDIT", atm!= $atm1 )
not Operation(account == $acc, atm != $atm1, bank == $b,
this before $op2, this after $op1 )
not Monitor( bank == $bank, account == $acc )
then
insert( new Monitor( $bank, $acc, $amt1 + $amt2, "CREDIT" );
end
you have a Monitor inserted. The monitor has the obvious fields and a state field, set to "CREDIT".
rule "add deposit"
when
$op1: Operation( $a:account, $b:bank, $amt1:amount, operation‌​=="CREDIT", $atm1: atm )
$mon: Monitor( account==$a, bank==$b, $bal: balance, state == "CREDIT" )
then
// another deposit
modify( $op1 ){ setAmount( $bal + $amt1 ) }
end
With this event, the state changes to withdrawal.
rule "state changes to withdrawal"
when
$op1: Operation( $a:account, $b:bank, $amt1: amount, operation‌​=="WITHDRAW", $atm1: atm )
$mon: Monitor( account==$a, bank==$b, $bal: balance, state == "CREDIT" )
then
// switch to withdraw
modify( $op1 ){ setAmount( $bal + $amt1 ),
setState( "WITHDRAW" ) }
end
And here it begins to get messy. We need to check whether this (first) withdrawal cashes in on all deposits (balance == $amt1) or just reduces the balance or withdraws more than the previous sequence of deposits have made.
I quit here, but I think I've shown that this isn't possible to do with a single rule.

Related

I'm having trouble with the code where the budget variable wasn't deducting throughout the loop. It instead do nothing or sometimes add up

enter image description hereI'm making an inventory system code and I'm a bit stuck finding a solution to the substraction problem, when I choose "ADD" and entering the input the formula wasn't getting the accurate outcome. For example, if I input Paper001 then its quantity, the output is fine at first but when input another item, the deduction instead becoming addition or sometimes doesn't do anything.
I tried dividing the values in the dictionary in 3 conditions but it turns out even worse.
while True:
try:
bg = float(input("Enter your budget : "))
print("-----------------------------------------------------------")
print(" Item name Item code Item price(Per set) \n")
print("1.Bond Paper : Paper001 100 PHP\n2.Yellow Paper : Paper002 50 PHP\n3.Intermediate Paper : Paper003 20 PHP\n\n")
s = bg
except ValueError:
print("PRINT NUMBER AS A AMOUNT")
continue
else:
break
a ={"name":[], "quant":[], "price":[]}
# converting dictionary to list for further updation
b = list(a.values())
# variable na value of "name" from dictionary 'a'
na = b[0]
# variable qu value of "quant" from dictionary 'a'
qu = b[1]
# This loop terminates when user select 2.EXIT option when asked
# in try it will ask user for an option as an integer (1 or 2)
# if correct then proceed else continue asking options
while True:
try:
print("-----------------------------------------------------------")
ch = int(input("1.ADD\n2.STORAGE LIST\n3.Customer purchase\n4.EXIT\nEnter your choice : "))
except ValueError:
print("\nERROR: Choose only digits from the given option")
continue
else:
# check the budget is greater than zero and option selected
# by user is 1 i.e. to add an item
if ch == 1 and s>0:
p_list={'Paper001':100,'Paper002':50,'Paper003':20}
pn = input("Enter item code : ")
if pn in p_list.keys():
q = int(input("Enter quantity : "))
else:
print("-----------------------------------------------------------")
print("Code is invalid")
continue
#checks if item name is already in the list
if pn in na:
ind = na.index(pn)
# remove quantity from "quant" index of the product
qu.remove(qu[ind])
# new value will be added to the previous value of user's quantity
qu.insert(ind, q)
tpr = (q+q)*p_list[pn]
print(f" Total product price:",tpr)
s = bg-tpr
print("\namount left", s)
else:
# append value of in "name", "quantity", "price"
na.append(pn)
# as na = b[0] it will append all the value in the
# list eg: "name"🙁"rice"]
qu.append(q)
# after appending new value the sum in price
# as to be calculated
tpr = q*p_list[pn]
print("\nTotal product price:",tpr)
s = bg-tpr
if s==0:
print("No more budget left")
print("\nAmount left :", s)
elif s>0:
print("\nAmount left :", s)
else:
print("Insufficient budget. Cannot buy item.")
print("Please restart and re-enter your budget.")
break
elif ch ==2 :
print("\n\n\nStorage list")
print("Item name Stocks ")
for i in range(len(na)):
print(f"{na[i]} {qu[i]}")
continue
elif ch == 3:
print("-----------------------------------------------------------")
p_list={'Paper001':100,'Paper002':50,'Paper003':20}
print("\n\n\nStorage list")
print("Item name Stocks ")
for i in range(len(na)):
print(f"{na[i]} {qu[i]}")
co = input("\nEnter customer's order : ")
if co in p_list.keys():
q = int(input("Enter quantity : "))
sl = qu[i]-q
print("Item is sold!")
print("Stocks left :",sl)
if sl <=0:
print("Please add new items!")
else:
print("-----------------------------------------------------------")
print("Code is invalid")
continue
elif ch==4:
print("")
print("\nThank your for shopping with us!")
break
elif s==0:
print("NO BUDGET LEFT. UNABLE TO ADD ITEMS.")
else:
print("ERROR: Choose only the digits given from the option.")

Drools variable binding: reassignment not allowed

Assume that MyObject is a Java Object with an integer property called integerProperty.
I would like to write a Drools rule like the following (syntactically incorrect):
rule "myRule"
when
MyObject( $integerProperty : integerProperty )
accumulate(
$o : MyObject(
integerProperty == $integerProperty + 1,
$integerProperty : this.integerProperty
);
$total : count($o);
$total > 10
)
then
[BLABLABLA]
end
I want to fire my rule when there is a set of more than 10 elements of type MyObject whose integerProperty values form an arithmetic sequence, e.g. 1, 2, 3, … In this case, I will assign a negative value to an HardMediumSoftLongScore object (hoping that information this helps).
I am sure that an MyObject having a given integerProperty is unique (eg. there is just one MyObject having integerProperty equal to, say, 2). The problem is that I'm not allowed to re-assign the variable binding $integerProperty. Is there a way around this?
Try implementing the below example and check whether you are getting your desired output. Before firing the rule insert the previous object as a global variable (prev in the example below) in the drool session.
global MyObject prev;
rule "myRule"
when
$total : Number(doubleValue > 10) from accumulate(
$o : MyObject( prev.getIntegerProperty() != null && $i : integerProperty == prev.getIntegerProperty() + 1),
init( double total = 0;),
action( total += 1; ),
reverse( total -= 1; ),
result( new Double( total ) ) )
then
[BLABLABLA]
end
In your java code before firing the rule set the previous object as a global object. Follow the code below :
kieSession.setGlobal("prev",myprevobject)
kieSession.insert(myobject)
kieSession.fireAllRules()

Drools - Accumulate logic

I need help in writing the accumulate logic for below requirement:
Requirement: Certain rules will provide the percentage to be applied for global value. Another set of rules should use the aggregate total percentage in determining the result.
For example: 0.75 is the global value passed as input(threshold).
Rule 1 might apply -10% of the fixed value ie. 0.75 - (0.75 * 0.10) = 0.675
Rule2 will apply + 20% off updated value. ie., 0.675 + (0.675 * 0.20) = 0.81
My global value is 0.75 (Threshold)
Using the below rules I am trying to applied the percentage applicable for a fixed value :
//class imports
global Double FIXED_THRESHOLD;
//Rules
rule "Prediction Rule_2"
lock-on-active true
no-loop true
salience (2)
when
LossInput (airBagDeployed == 'Y' , driveable == 'N')
result : RuleResult(predictedTotalsThreshold == 0)
then
insert(new ControlFact( -10.0 ) ); //Reduce -10% to global value 0.75 - 0.75* 0.10 = 0.675
System.err.println("New control fact added to working memory....");
end
rule "Prediction Rule_1"
lock-on-active true
no-loop true
salience (1)
when
LossInput (airBagDeployed == 'Y' , driveable == 'N', make == 'Honda' )
result : RuleResult(predictedTotalsThreshold == 0)
then
insert(new ControlFact( 20.0 ) ); // Add 20% to the updated aggregate (0.20 % of 0.675).
System.err.println("New control fact added to working memory....");
end
I tried the below accumulate logic but obviously it is wrong. It is applying only to fixed value always instead of the updated value.
rule "Aggregate All Threshold"
no-loop true
when
$aggregateTotalsThresholdPercentage : Number() from accumulate(
ControlFact( $totalsThreshold : totalsThresholdPercentage ),
sum( ( FIXED_THRESHOLD + ( FIXED_THRESHOLD * $totalsThreshold ) / 100 ) ) )
ruleResult: RuleResult(predictedTotalsThreshold == 0)
then
ruleResult.setPredictedTotalsThreshold($aggregateTotalsThresholdPercentage.doubleValue());
update(ruleResult);
end
POJO:
public class LossInput{
private String airBagDeployed;
private String driveable;
private String make;
}
public class ControlFact {
public double totalsThresholdPercentage;
}
public class RuleResult {
private double predictedTotalsThreshold;
}
//insert facts in working memory
kieSession.insert(lossInput);
kieSession.insert(ruleResult);
kieSession.setGlobal("FIXED_THRESHOLD", new Double(0.75));
kieSession.fireAllRules();
Please help on the accumulate logic to apply the updated value everytime when percentage threshold to be applied.
You cannot use accumulate/sum this way because you add the FIXED_THRESHOLD for each ControlFact.
Insert ControlFacts as you have in the "Prediction..." rules (without all the rule attributes). Use each ControlFact to update RuleResult's predictedTotalsThreshold. The "Aggregate" rule will fire repeatedly, and therefore you need to make sure to retract the used ControlFact.
rule "Aggregate All Threshold"
when
ControlFact( $ttp: totalsThresholdPercentage );
$res: RuleResult( $ptt: predictedTotalsThreshold)
then
double nt = $ptt + $ptt*$ttp/100;
modify( $res ){ setPredictedTotalsThreshold( $nt ) }
retract( $res );
end

Is it possible to count the number of occurrences of a calculated field's result in Tableau?

I have a function that categorizes calls a user has made into 3 categories using this calculation:
IF 0 <= DATEDIFF('dayofyear', [SubmittedDateTime], [CALLDATE])
AND DATEDIFF('dayofyear', [SubmittedDateTime], [CALLDATE]) <= 7
THEN "Week After"
ELSEIF -7 <= DATEDIFF('dayofyear', [SubmittedDateTime], [CALLDATE])
AND DATEDIFF('dayofyear', [SubmittedDateTime], [CALLDATE]) < 0
THEN "Week Before"
ELSE "Not within a week"
END
I was wondering if it's possible to count the number of occurrences of a particular outcome of the function on a per user basis in order to then categorize each user based off of the number of occurrences. I'm attempting to use this calculation to do so:
IF { FIXED [SUBID]: COUNT([DateDiff Calc] = 'Week After')} = 1
THEN "1 Conference User"
ELSEIF { FIXED [SUBID]: COUNT([DateDiff Calc] = 'Week After') } > 1
THEN "Multiple Conference User"
ELSE "0 Conference User"
END
but the COUNT function I'm using is not working properly it seems. It seems that the COUNT function is also counting occurrences of both "Week Before" and "Not within a week" from the first function.
I think the problem is the measure part of your LOD expression :
COUNT([DateDiff Calc] = 'Week After')
This will just give you count of both times: when your conditions is met and when its not met. [DateDiff Calc] = 'Week After' will return true or false, both will be counted as +1 in the count function.
You could try something like:
IF { FIXED [SUBID]: SUM(IF[DateDiff Calc] = 'Week After' THEN 1 ELSE 0 END)} = 1
THEN "1 Conference User"
...

Optaplanner newbie: nurse softconstraint for weekends

I'm studying Optaplanner, and am doing some experiments with the Nursing Roster.
My goal, for this experiment, is simple: to have nurse "1" be more in favor, and more likely, to work weekends.
I have written the following rules to help make this happen:
rule "nurseNamed1WorksWeekends"
when
$oneNurse: Employee( name = "1")
$wk : ShiftAssignment( isWeekend = true)
then
scoreHolder.addSoftConstraintMatch(kcontext, 1);
end
rule "nurseNamed1MustNotWorkWeekdays"
when
$oneNurse: Employee( name = "1")
not $wk : ShiftAssignment( isWeekend = false)
then
scoreHolder.addSoftConstraintMatch(kcontext, 1);
end
However, after running the sample for some time, nurse "1" still never ends up working weekends.
What am I doing wrong?
Thanks
Edit of rule according to laune's suggestions but optaplanner is still reluctant to put the nurse on weekend shifts:
rule "nurseNamed1WorksWeekends"
when
$oneNurse: Employee( name == "1", )
$wk : ShiftAssignment( isWeekend == true, employee == $oneNurse)
then
scoreHolder.addSoftConstraintMatch(kcontext, 1);
end
rule "nurseNamed1MustNotWorkWeekdays"
when
$oneNurse: Employee( name == "1")
not ShiftAssignment( isWeekend = false, employee == $oneNurse)
then
scoreHolder.addSoftConstraintMatch(kcontext, 1);
end
Don't use = in your constraints - test for equality is expressed using ==.
If the getter for a boolean is called isWeekend, the constraint should be written as
ShiftAssignment( weekend == true )
ShiftAssignment( weekend == false )
or, (for me) preferably
ShiftAssignment( weekend )
ShiftAssignment( ! weekend )
A binding variable in a Conditional Element such as $wk in
not $wk : ShiftAssignment( ! isWeekend )
doesn't make sense. The rule fires if there is no such ShiftAssignment - and then what would $wk being bound to?
The CE
not ShiftAssignment( ! weekend )
is strange: the rule fires if and only if there is no ShiftAssignment for any weekday around at all - not likely.
Adding a value higher than one in the "WorksWeekends" rule should favour nurse 1 on weekends.
Later
rule dislikeNurseOneOnWeekdays
when
$oneNurse: Employee( name == "1")
ShiftAssignment( isWeekend = false, employee == $oneNurse)
then
scoreHolder.addSoftConstraintMatch(kcontext, -1);
end
Using a smaller value (e.g. -10) will make it even harder for the First Nurse to work on weekdays: ten shifts during the weekend are needed to balance one during the week.