Size and contains methods of List in Drools - drools

Is it possible write conditions on the basis of size of a list using guided editors (either guided decision table or guided rule) and to check if any element is present in the list?
For eg.
User {
List of badges;
}
now I want to apply condition on number of badges and if a specific badge is in user's list of badges.
Also can I compare to objects. For eg. condition on count of badges of two users?
I'm using KIE Drools Workbench
Here's how source code should look like :-
import com.demo.dto.User;
import com.demo.dto.Badges;
rule "Compare two variables"
when
$badge : Badges()
$user : User(name == "Sam", badges.size() >= 9, $badge memberOf badges)
then
$user.setService($user.getService() + $user.getBadges().size());
end

Related

ipywidgets, problem using a Text widget to filter options in a Select widget with automatic updates/ real-time interaction

I'm trying to use ipywidgets for the first time to make an interactive browser in Jupyter Notebooks of Census data using the census module. In general I'm having difficulty learning the widget library. My plan is to use the Text widget to enter a search term that will then filter the available census 'concepts' or table options (example: "Means of transportation to work") in a Selection or SelectMultiple widget. The selections made will then filter and show available data variables / columns for that census 'concept' or table (example: "car", "truck", "bus", "bike", "walk")
For reference, this first code block is what I got to work in a normal Jupyter Notebook with no widgets (at the bottom is the ipywidget attempt I'm having trouble with). I'm just starting to explore widgets and don't know anyone with experience with them. Any assistance is greatly appreciated!
*Note, running my code would require a census api key from census.gov/data/developers.html, the "Request a KEY" option on the left side menu, and change the ckey="" variable in the code below to your key. Keys are free, as it is publicly available government data.
#This section works for me as standalone code, not using ipywidgets
#Show all concepts to get an idea of search phrases to use
concepts_all = list([n for n in sorted(list([f"{t['description']}" for t in c.acs5.tables()]))]);
#This is a VERY long list, so I commented out printing it
#[print(c) for c in concepts_all]
#Apply a search phrase and limit options accordingly
concept_description_search_phrase = "Means of transportation to work by travel"
[print(f"{table['name']}: {table['description']}") for table in c.acs5.tables() if concept_description_search_phrase.lower() in table['description'].lower()]
#From the filtered options, chose one specific one to search, and display available data for it
concept_full_description = 'MEANS OF TRANSPORTATION TO WORK BY TRAVEL TIME TO WORK'
variable_filter_phrase = 'bicycle'
df = [pd.read_json(table['variables']) for table in c.acs5.tables() if table['description'] == concept_full_description][0]
acs_variable_names_list = [row.name for n,row in df.iterrows()
if variable_filter_phrase.lower() in row['variables']['label'].lower()
and 'Annotation' not in row['variables']['label']
and 'Margin of Error' not in row['variables']['label']]
acs_variable_names_list
# This is the final list I want out from a ipywidgets based workflow
# Which are variable columns, (ex: number of people in each census block group that
# have a 10, 15, 30, 45, 60+ minute commute by bicycle
########################################################
I'm having trouble getting the ipywidgets to work properly, where the Text box will interactively filter the available options in the Selection menu. I can only get it to work on a default value for the Text widget, but not off of the interactive menu. I'm basically going for the effect of the Combobox widget, but with the option to select more than one item if desired, and still see all the available options after a selection is made (so I'm thinking to use a Text and Select widget together instead of Combobox). This is my attempt so far:
out = widgets.Output()
display(out)
search_phrase = widgets.Text(
value = 'transportation',
layout = widgets.Layout(width = '900px')
)
select_multiple = widgets.SelectMultiple(
options = [n for n in concepts_all if search_phrase.value.lower() in n.lower()],
layout = widgets.Layout(width='900px',height='250px'),
ensure_option = True,
disabled=False
)
def search_change(change):
output.clear_output()
display(search_phrase)
with output:
if change.new == '':
print('new search')
return search_phrase.value
else:
print('same search')
return search_phrase.value
display(search_phrase)
display(select_multiple)
#Issue here with getting the search_phrase to properly filter options in select_multiple
#
concept_full_description = select_multiple.value[0] #Using Widgets
variable_filter_phrase = widgets.Text(value = 'bicy')
display(variable_filter_phrase) #Issue, this text box won't automatically filter the output
#it is only filtering by the default value
df = [pd.read_json(table['variables']) for table in c.acs5.tables() if table['description'] == concept_full_description][0]
acs_variable_names_list = [row.name for n,row in df.iterrows()
if variable_filter_phrase.value.lower() in row['variables']['label'].lower()
and 'Annotation' not in row['variables']['label']
and 'Margin of Error' not in row['variables']['label']] #print(row.name,": ",row['variables']['label'])
acs_variable_names_list
#This is the desired output list that will go into later workflows
I think I'm doing something wrong either in use of the .observe(), .interact(), or output /clear_output process. I'm just learning the ipywidgets and am even having trouble getting this to work in a basic example trying to follow the ipywidgets.readthedocs.io documentation.

Drools generate filters on rule dinamically

New with JBoss Drools :)
I'm working on a PoC where there's a rule where one or multiple filters can exist. I'm using a template to generate that rule dynamically.
template header
effectiveDate
expirationDate
rule
totalPriceValue
package test.template;
import test.domain.ProductDiscountRequest;
template "value discount template"
rule "Value discount on customer_#{row.rowNumber}"
date-effective "#{effectiveDate}"
date-expires "#{expirationDate}"
when
requestObject : ProductDiscountRequest(#{rule} && totalPrice > #{totalPriceValue});
then
requestObject.addDiscount(5.0d);
end
end template
This template, with the correct input, generates a rule like
package test.template;
import test.domain.ProductDiscountRequest;
rule "Value discount on customer_0"
date-effective "25-Jun-2022 16:00"
date-expires "26-Jun-2030 8:00"
when
requestObject : ProductDiscountRequest(customer == 'TestCustomer' && totalPrice > 30);
then
requestObject.addDiscount(5.0d);
end
There are multiple filters available and they can also be combined, so customer == 'TestCustomer' can also be customer == 'TestCustomer' && custGroup == "TestGroup" and so forth.
I see 3 solutions:
Generate the filtering part in the code and only insert it
Create distinct rules per number of filters (1 rule for 1 filter, 1 rule for 2 filters etc)
Send the filters to the template and generate it there directly
I tried some things but I couldn't get #3 to work. Supposing that there's a list of filters, how do I iterate over it and concatenate it to a full condition?
I'm open to other ideas to make this better.

Tableau : Filter Value

I have below data for a filter.
But need some rollup and create new filter list values as
Online (includes App, Desktop and Mobile) and
Offline (includes Kiosk and Pos)
Any Suggestions.
Set up a parameter (i.e., "PSource") with all of the values that you want to have (original plus groups) and make sure it is shown on your worksheet or dashboard. Create a calculated field (i.e., "CSource") where "Source" is the assumed name of the field in your data that includes your original values:
CASE [PSource Parameter]
WHEN "Online (All)" THEN IIF(FIND("App/Desktop Browser/Mobile Browser",[Source])>0,"T","F")
WHEN "Offline (All)" THEN IIF(FIND("Kiosk/Pos",[Source])>0,"T","F")
WHEN "App" THEN IIF([Source]="App","T","F")
WHEN "Desktop Browser" THEN IIF([Source]="Desktop Browser","T","F")
WHEN "Mobile Browser" THEN IIF([Source]="Mobile Browser","T","F")
WHEN "Kiosk" THEN IIF([Source]="Kiosk","T","F")
WHEN "Pos" THEN IIF([Source]="Pos","T","F")
END
Drop CSource onto the Filters shelf and select the "T" value.

Drools - Running a rule with an empty object

I'm trying to write a rule to calculate prices for an insurance product based on conditions. In the 'when' I'm using an object called AdditionalDriver, which contains the details for drivers other than the policy holder. From this, different prices can be calculated based on whether the additional driver is a parent, friend, spouse etc. See below:
when
AdditionalDriver($relToProp : relationToProposer)
then
String relToProp = $relToProp;
if(!relToProp.equals("P"))
{
//prices
}
end
"P" = parent.
This rule works when an additional driver has been added. However, if there is no additional driver, then the object is empty, and so the rule does not run. What do I need to do to get this rule to run, even when the object is empty?
Thanks in advance.
You should write one rule for each of the relative or acquaintance classes:
when
PolicyHolder( $phid: id )
AdditionalDriver( relationToProposer == "P", belongsTo == $phid )
then
//prices
end
For no additional driver being requested, write a rule
when
PolicyHolder( $phid: id )
not AdditionalDriver( belongsTo == $phid )
then
// cheaper prices
end
Don't use conditional statement in your consequences to further distinguish facts. This is a code smell.

UIMA Ruta: Copy the feature value from a contained annotation to a containing annotation

Note: This seems heavily related to Setting feature value to the count of containing annotation in UIMA Ruta. But I cannot quite apply the answer to my situation.
I am analyzing plain text documents where the following structure is assumed:
Document (one, of course)
Section (many)
Heading (one per section)
I am being asked to identify sections by checking whether their headings satisfy conditions. A useful and obvious sort of condition would be: does the heading match a given regular expression? A less-useful but perhaps more achievable condition would be: does the heading contain a given text?
I could and have already achieved this by taking a list of tuples of regular expressions and section titles, and at design time, for each member of the list, as such:
BLOCK(forEach) SECTION{} {
...
HEADING{REGEXP(".*table.*contents.*", true) ->
SETFEATURE("value", "Table of Contents")};
...
}
SECTION{ -> SETFEATURE("value", "Table of Contents")}
<- { HEADING.headingValue == "Table of Contents"; };
This approach is fairly straightforward but has a few big drawbacks:
It heavily violates the DRY principle
Even when writing the rule for just one section to identify, the rule author must copy the section title twice (it should only need to be specified once)
It makes the script needlessly long and unwieldy
It puts a big burden on the rule author, who in an ideal case, would only need to know Regex - not Ruta
So I wanted to refactor to achieve the following goals:
A text file is used to store the regular expressions and corresponding titles, and the rule iterates over these pairs
Features, rather than types, are used to differentiate different sections/headings (i.e. like above, using SECTION.value=="Table of Contents" and not TableOfContentsSection)
After looking over the UIMA Ruta reference to see which options were available to achieve these goals, I settled on the following:
Use a WORDTABLE to store tuples of section title, words to find / regex if possible, lookup type - so for instance, Table of Contents,contents,sectiontitles
Use MARKTABLE to mark an intermediate annotation type LookupMatch whose hint feature contains the section title and whose lookup feature contains the type of lookup we are talking about
For each HEADING, see if a LookupMatch.lookup == "sectiontitle" is inside, and if it is, copy the LookupMatch.hint to the heading's value field.
For each SECTION, see if a HEADING with a value is inside; if so, copy the value to the SECTION.value field.
It was not quite a surprise to find that implementing steps 3 and 4 was not so easy. That's where I am at and why I am asking for help.
// STEP 1
WORDTABLE Structure_Heading_WordTable =
'/uima/resource/structure/Structure_Heading_WordTable.csv';
// STEP 2
Document.docType == "Contract"{
-> MARKTABLE(LookupMatch, // annotation
2, // lookup column #
Structure_Heading_WordTable, // word table to lookup
true, // case-insensitivity
0, // length before case-insensitivity
"", // characters to ignore
0, // matches to ignore
"hint" = 1, "lookup" = 3 // features
)
};
// STEPS 3 AND 4 ... ???
BLOCK(ForEach) LookupMatch.lookup == "sectiontitle"{} {
???
}
HEADING{ -> SETFEATURE("value", ???)} <- {
???
};
Here is my first real stab at it:
HEADING{ -> SETFEATURE("value", lookupMatchHint)} <- {
LookupMatch.lookup == "HeadingWords"{ -> GETFEATURE("hint", lookupMatchHint)};
};
TL; DR
How can I conditionally copy a feature value from one annotation to another? GETFEATURE kind of assumes that you only get 1...