Logic: Rule Types, Patterns
TL;DR - Logic: multi-table derivations and constraints, using Rules and Python
Logic addresses multi-table derivations and constraints, using Rules and Python. Rules are:
- Declared with WebGenAI or IDE and Code Completion - 40X more concise
- Activated on server start
- Executed - automatically - on updates (using SQLAlchemy events)
- Debugged in your IDE, and with the console log
For more on WebGenAI, click here.
Rule Types
The table shows excerpts only; see the ApiLogicProject
(Northwind) sample for full syntax.
Rule | Summary | Example | Notes |
---|---|---|---|
Constraint | Boolean function must be True else transaction rolled back |
row.Balance <= row.CreditLimit row.Salary >= Decimal('1.20') * old_row.Salary |
Multi-fieldold_row |
Formula | Function computes column value | row.UnitPrice * row.Quantity row.OrderHeader.ShippedDate |
lambda, or function Parent (OrderHeader) references |
Sum | Derive parent-attribute as sum of designated child attribute; optional child qualification | Rule.sum(derive=Customer.Balance, as_sum_of=Order.AmountTotal,where=lambda row: row.ShippedDate is None) |
Parent attribute can be hybrid (virtual) scalable: pruning, adjustment |
Count | Derive parent-attribute as count of child rows; optional child qualification | Rule.count(derive=Order.OrderDetailCount, as_count_of=OrderDetail) |
counts are useful as child existence checks |
Copy | Child value set from Parent | OrderDetail.ProductPrice = copy(Product.Price) |
Unlike formula references, parent changes are not propagated e.g, Order totals for Monday are not affected by a Tuesday price increase |
Event | Python Function | on insert, call congratulate_sales_rep |
See Extensibility for a information on early, row and commit events |
Parent Check | Ensure Parent row exists | Orders must have a Customer | See Referential Integrity |
Allocation | Allocate a provider amount to recipients | allocate a payment to outstanding orders | See Allocation for an example |
Copy Row | Create child row by copying parent | audit Employee Salary changes to EmployeeAudit | See Rule Extensibility |
Declaring Rules
The table below illustrates that:
- You can declare rules in Natural Language using WebGenAI and/or Python.
- Rules are stored in your project depending on how they were defined
Rules Declared In | Rules are stored in |
---|---|
WebGenAI, using Natural Language | * logic/wg_rules - see WebGenAI Rules and IDE Rules |
Your IDE, using Python (as a DSL) | * logic/declare_logic.py * logic/logic_discovery - see here |
GenAI: Natural Language Logic
You can use Natural Language to create logic during project creation, or for existing projects. For example: The Customer's balance is the sum of the Order amount_total where date_shipped is null
.
- For more information on using Natural Language Logic, see Natural Language Logic.
- For more information on Managing IDE logic and Natural Language Logic, see WebGenAI Rules and IDE Rules.
Think of Natural Language Logic as a translation process down onto underlying rules -- not a code generator.
It is important to learn the rules described here, and to verify proper translation of Natural Language Logic.
IDE: With Code Completion
You can also use your IDE with Code Completion to add rules, and their arguments.
IDE: Natural Language
You can use Natural Language in your IDE. See IDE: Natural Language.
Iterative Rules
Logic definition is an incremental process. You can start with a few rules, and add more as needed. There is no need to define all rules at once, or rebuild the project.
Note rules are automatically ordered and invoked, so you can add new ones in any location.
Similarly, you can change rules without worrying about the order of execution.
Learning Rules
Inside the larger process above, here is the best way to learn how to use rules:
-
Rule Summary: review the Rule Types table above; there are a small number of rules, since their power lies in chaining
- Alert: Logic consists of rules and Python. You will quickly learn to use logic events; focus on the rules as the preferred approach, using Python (events, etc) as a fallback.
-
Review the Rule Patterns, below
-
Use the case study approach to learn about using rules, by exploring the examples in the report, below.
-
Be aware of Rule Extensibility.
Pre-req: before learning rules, use the Tutorial to familiarize yourself with basic capabilities and procedures.
Rule Patterns
Rules support chaining: a rule may change a value that triggers other rules, including across tables. Mastering such multi-table logic is the key to using rules effectively. The most typical examples are described below.
Pattern | Notes | Example |
---|---|---|
Chain Up | parent sums and counts mean that child row changes can adjust parents | Derive Balance |
Constrain a Derived Result | constraints may require derived values | Balance < creditLimit |
Chain Down | child copy and parent references mean that parent row changes can cascade to children | Ship Order |
State Transition Logic | old_row useful comparing old/current values |
Meaningful Raise |
Counts as Existence Checks | Use counts to check if any children exist | Don't Ship Empty Orders |
Auditing | Note the Copy Row rule (and alternatives) | Salary Audit |
Ready Flag | Multi-session editing, then , when ready... adjust related data / enforce constraints |
Make Order Ready |
Events for Lib Access | Events enable Python, use of standard libs (e.g., Kafka) | Ship Order |
Rules Case Study
The best way to learn the rules is by a Case Study approach:
-
Print this page, for reference
-
Print the Database Diagram
- Most of the examples are drawn from this database
-
For each Rule Pattern, above:
-
Click the Example link in the table above to open the Behave Logic Report
- Aside: later, you can prepare such documentation for your own projects, (like this).
-
Review the Scenario -- take these as your requirements
-
Spend 5 minutes (perhaps in pairs) and cocktail-napkin design your solution, using
- The data model diagram
- List of Rule Types, and
- Rule Patterns
-
Reveal the solution: open the disclosure box: "Tests - and their logic - are transparent.. click to see Logic"
-
Learning Natural Language
As noted above, it is important to be clear on the rules generated for logic. Use the examples below to test your understanding.
WebGenAI provides the Logic Editor so you can see/edit the translation:
Natural Language Patterns
Pattern | Notes | Example |
---|---|---|
Formal vs Informal | You can: Customer.balance = Sum(Order.amount_total where date_shipped is null) | Or, more simply: The Customer's balance is the sum of the Order amount_total where date_shipped is null |
Integration Logic | Kafka | Send the Order to Kafka topic 'order_shipping' if the date_shipped is not None |
Multi-rule Logic | See Multi-rule Logic - Generated Rules, below | Sum of employee salaries cannot exceed department budget |
Conditional Derivations | See Conditional Derivation - Generated Rules, below | Provide a 10% discount when buying more than 10 carbon neutral products The Item carbon neutral is copied from the Product carbon neutral |
Cardinality Patterns - Qualified Any |
See Cardinality Patterns - Generated Rules, below | Products have Notices, with severity 0-5. Raise and error if product is orderable == True and there are any severity 5 Notices, or more than 3 Notices. |
Multi-rule Logic - Generated Rules
# Aggregate the total salaries of employees for each department.
Rule.sum(derive=Department.total_salaries, as_sum_of=Employee.salary)
# Ensure the sum of employee salaries does not exceed the department budget
Rule.constraint(validate=Department, as_condition=lambda row: row.total_salaries <= row.budget, error_msg="xxx")
# End Logic from GenAI
Conditional Derivation - Generated Rules
# Provide a 10% discount when buying more than 10 carbon neutral products.
Rule.formula(derive=Item.amount,
as_expression=lambda row: 0.9 * row.unit_price * row.quantity \
if row.Product.is_carbon_neutral and row.quantity > 10
else row.unit_price * row.quantity)
# End Logic from GenAI
Cardinality Patterns - Generated Rules
# Logic from GenAI: (or, use your IDE w/ code completion)
# Derive product notice count from related notices.
Rule.count(derive=Product.notice_count, as_count_of=Notice)
# Derive count of severity 5 notices for products.
Rule.count(derive=Product.class_5_notice_count, as_count_of=Notice, where=lambda row: row.severity == 5)
# Ensure product is not orderable if conditions on notices are met.
Rule.constraint(validate=Product,
as_condition=lambda row: not (row.orderable and (row.class_5_notice_count > 0 or row.notice_count > 3)),
error_msg="Orderable product contains severity 5 or excessive notices.")
# End Logic from GenAI
Natural Language Examples
WebGenAI was trained to understand the Natural Language Logic problems shown below. These automate many of the rule patters described above.
Please see Natural Language Logic.
Example | Notes |
---|---|
Airport - at least 10 tables A flight's passengers must be less than its Airplane's seating capacity |
|
System for Departments and Employees. Sum of employee salaries cannot exceed department budget |
|
Create a system with Employees and their Skills. More than One Employee can have the same Skill. EmployeeSkill.rating = Skill.rating An Employee's skill-rating is the sum of the Employee Skills rating, plus 2 * years of service. |
|
Students have probations and sick days. Signal an error if a Student's can-graduate is True, and there are more 2 probations, or more than 100 sick days. |
|
Applicant have felonies and bankruptcies. Signal error if is-hirable is true and there are more than 3 bankruptcies, or 2 felonies. |
|
Students have Grades and belong to Clubs. Copy the name from Club to Student Club The student's service activity is the count of Student Clubs where name contains 'service'. Signal error if student is eligible for the honor society == True, and their grade point average is under 3.5, or they have less than 2 service activities |
|
Products have Notices, with severity 0-5. Raise and error if product is orderable == True and there are any severity 5 Notices, or more than 3 Notices. |
|
Create a system with customers, orders, items and products. Include a notes field for orders. Use Case: enforce the Check Credit for ready orders: 1. Customer.balance <= credit_limit 2. Customer.balance = Sum(Order.amount_total where date_shipped is null and ready is True) 3. Order.amount_total = Sum(Item.amount) 4. Item.amount = quantity * unit_price 5. Store the Item.unit_price as a copy from Product.unit_price Use Case: Compute Products ordered 1. Item.ready = Order.ready 2. Product.total_ordered = sum(Item.quantity) where ready == True 3. Product.reorder_required = quantity_on_hand <= total_ordered Use Case: No Empty Orders 1. Order.item_count = Count(Items) 2. When setting the date_shipped, item_count must be > 0. |
Ready Flag |
Teachers, courses which have offerings, and students who have offerings. Use Case: capacity teachers cannot be assigned to more than 5 courses students cannot have more enrollments than 6 Use Case: budget control courses have a charge, which is copied to enrollments charge a student's total enrollment charges cannot exceed their budget |