-
Notifications
You must be signed in to change notification settings - Fork 0
/
nw_rules_bank.py
119 lines (85 loc) · 4.75 KB
/
nw_rules_bank.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import sqlalchemy
from sqlalchemy_utils import get_mapper
from python_rules.exec_row_logic.logic_row import LogicRow
from python_rules.rule import Rule
from python_rules.rule_bank.rule_bank import RuleBank
from nw.nw_logic import models
from nw.nw_logic.models import Customer, OrderDetail, Product, Order, Employee
def activate_basic_check_credit_rules():
"""
Issues function calls to activate check credit rules, below.
These rules are executed not now, but on commits
Order is irrelevant - determined by system based on dependency analysis
Their inclusion in classes is for doc / convenience, no semantics
These rules apply to all transactions (automatic re-use), eg.
* place order
* change Order Detail product, quantity
* add/delete Order Detail
* ship / unship order
* delete order
* move order to new customer, etc
"""
def units_in_stock(row: Product, old_row: Product, logic_row: LogicRow):
result = row.UnitsInStock - (row.UnitsShipped - old_row.UnitsShipped)
return result
def congratulate_sales_rep(row: Order, old_row: Order, logic_row: LogicRow):
if logic_row.ins_upd_dlt == "ins" or True: # logic engine fills parents for insert
sales_rep = row.SalesRep # type : Employee
if sales_rep is None:
logic_row.log("no salesrep for this order")
else:
logic_row.log(f'Hi, {sales_rep.Manager.FirstName}, congratulate {sales_rep.FirstName} on their new order')
Rule.constraint(validate=Customer,
as_condition=lambda row: row.Balance <= row.CreditLimit,
error_msg="balance ({row.Balance}) exceeds credit ({row.CreditLimit})")
Rule.sum(derive=Customer.Balance, as_sum_of=Order.AmountTotal,
where=lambda row: row.ShippedDate is None) # *not* a sql select sum...
Rule.sum(derive=Order.AmountTotal, as_sum_of=OrderDetail.Amount)
Rule.formula(derive=OrderDetail.Amount, as_expression=lambda row: row.UnitPrice * row.Quantity)
Rule.copy(derive=OrderDetail.UnitPrice, from_parent=Product.UnitPrice)
Rule.formula(derive=OrderDetail.ShippedDate, as_exp="row.OrderHeader.ShippedDate")
Rule.sum(derive=Product.UnitsShipped, as_sum_of=OrderDetail.Quantity,
where="row.ShippedDate is not None")
Rule.formula(derive=Product.UnitsInStock, calling=units_in_stock)
Rule.commit_row_event(on_class=Order, calling=congratulate_sales_rep)
Rule.count(derive=Customer.UnpaidOrderCount, as_count_of=Order,
where=lambda row: row.ShippedDate is None) # *not* a sql select sum...
Rule.count(derive=Customer.OrderCount, as_count_of=Order)
class InvokePythonFunctions: # use functions for more complex rules, type checking, etc (not used)
@staticmethod
def load_rules(self):
def my_early_event(row, old_row, logic_row):
logic_row.log("early event for *all* tables - good breakpoint, time/date stamping, etc")
def check_balance(row: Customer, old_row, logic_row) -> bool:
"""
Not used... illustrate function alternative (e.g., more complex if/else logic)
specify rule with `calling=check_balance` (instead of as_condition)
"""
return row.Balance <= row.CreditLimit
def compute_amount(row: OrderDetail, old_row, logic_row):
return row.UnitPrice * row.Quantity
Rule.formula(derive="OrderDetail.Amount", calling=compute_amount)
Rule.formula(derive="OrderDetail.Amount", calling=lambda Customer: Customer.Quantity * Customer.UnitPrice)
Rule.early_row_event(on_class="*", calling=my_early_event) # just for debug
Rule.constraint(validate="Customer", calling=check_balance,
error_msg="balance ({row.Balance}) exceeds credit ({row.CreditLimit})")
class DependencyGraphTests:
"""Not loaded"""
def not_loaded(self):
Rule.formula(derive="Tbl.ColA", # or, calling=compute_amount)
as_exp="row.ColB + row.ColC")
Rule.formula(derive="Tbl.ColB", # or, calling=compute_amount)
as_exp="row.ColC")
Rule.formula(derive="Tbl.ColC", # or, calling=compute_amount)
as_exp="row.ColD")
Rule.formula(derive="Tbl.ColD", # or, calling=compute_amount)
as_exp="row.ColE")
Rule.formula(derive="Tbl.ColE", # or, calling=compute_amount)
as_exp="xxx")
class UnusedTests:
"""Not loaded"""
def not_loaded(self):
Rule.constraint(validate="AbUser", # table is ab_user
calling=lambda row: row.username != "no_name")
Rule.count(derive=Customer.OrderCount, as_count_of=Order,
where="ShippedDate not None")